基础知识:如下所示:
1.要想将类型从一个程序集移动到另一个程序集而不造成应用程序中断,可以使用System.Runtime.CompilerServices.TypeForwardedToAttribute特性在旧的程序集中传入新程序集中该类型的字符串,然后在新的程序集中传入旧程序集中该类型的字符串。
创建强命名程序集:如下所示:
1>.使用vs提供的SN.exe工具创建签名文件。
创建命令如下所示:
// 生成包含公钥和私钥对的签名文件MyCompany.snk
SN -k MyCompany.snk
// 将签名文件MyCompany.snk中的公钥数据以sha256算法写入到公钥文件MyCompany.PublicKey中
SN -p MyCompany.snk MyCompany.PublicKey sha256
// 显示公钥文件MyCompany.PublicKey中的公钥和公钥标记(对公钥进行哈希处理的后8字节数据)
SN -tp MyCompany.PublicKey
2>.使用csc.exe工具中的/keyfile命令来对包含元数据清单表的PE文件进行签名。
签名命令如下所示:
// 编译Program.cs文件生成PE文件,然后使用MyCompany.snk签名文件对该PE文件进行签名
csc /keyfile:MyCompany.snk Program.cs
签名流程如下所示:
1>>.生成包含元数据清单表的PE文件后,会对PE文件的内容进行哈希处理从而得到哈希值。
2>>.将得到的哈希值使用签名文件中的私钥进行签名从而得到RSA数字签名。
3>>.将得到的RSA数字签名存储到PE文件的保留区域。
4>>.将签名文件中的公钥存储到PE文件中的程序集清单表中。
5>>.引用了其他的强命名程序集时,就会将引用程序集的公钥标记存储到程序集引用表的Public Key字段中。
全局程序集缓存:一般位于%SystemRoot%\Microsoft.NET\Assembly目录下。具有以下特性:
1.net framework在安装时会将只包含元数据信息的程序集拷贝到编译器/CLR目录下当做编译时寻找程序集使用;同时会将包含元数据信息和IL代码的程序集拷贝到全局程序集缓存的子目录里面当做运行时加载程序集使用。
2.不要尝试在子目录里手动部署强命名程序集,因为子目录都是算法自动生成的,手动部署容易出错;此时可以使用GACUtil.exe工具来完成强命名程序集的部署操作。常见命令如下表所示:
命令 | 描述 |
---|---|
/i | 将某个强命名程序集安装到全局程序集缓存中。 |
/il | 将一个或者多个强命名程序集安装到全局程序集缓存中。 |
/u | 将某个强命名程序集从全局程序集缓存中卸载。 |
/ul | 将一个或者多个强命名程序集从全局程序集缓存中卸载。 |
/l | 列出通过指定程序集名字筛选出的全局程序集缓存。 |
/r | 指定要安装或者卸载的跟踪引用。 |
/lr | 列出通过指定程序集名字筛选出的全局程序集缓存以及所有跟踪引用。 |
/cdl | 删除下载缓存的内容。 |
/ldl | 列出下载缓存的内容。 |
/f | 强制重新安装程序集。 |
/nologo | 取消显示徽标版权标志。 |
/silent | 取消显示所有输出。 |
校验强命名程序集:将没有完全信任的强命名程序集安装到全局程序集缓存或者从非全局程序集缓存加载强命名程序集时,CLR会对该程序集进行安全校验。校验流程如下:
1.对PE文件中的元数据清单表进行哈希处理,得到哈希值。
2.使用PE文件中的公钥对PE文件中的RSA数字签名进行反向签名,得到哈希值。
3.比对1和2步骤中的哈希值是否相同,如果不同就说明程序集被篡改,否则就执行步骤4。
4.对程序集中的其他文件进行哈希处理,并将哈希值与文件清单表中存储的哈希值进行处理。任何一个哈希值不匹配,表明程序集至少一个文件被篡改。
延迟签名:由于签名文件中的私钥十分重要,所以一般都是严密保管的,只有少部分人有权限可以访问私钥。此时如果在开发和测试阶段使用私钥对程序集进行签名的话,审批流程繁琐,安全性也不高,所以可以先使用签名文件中的公钥来生成程序集,等到真正需要打包和部署时再用私钥对程序集进行签名。流程如下:
1.开发或者测试期间使用csc.exe工具的/keyfile和/delaysign开关编译程序集。此时生成的PE文件中存储了签名文件的公钥以及预留了RSA数字签名的空间。命令如下所示:
// 使用延迟签名技术将MyCompany.PublicKey公钥文件的内容写入到MyAssembly.dll程序集中并预留RSA数字签名空间
csc /keyfile:MyCompany.PublicKey /delaysign MyAssembly.cs
2.使用SN.exe工具的-Vr开关让CLR不校验程序集,从而可以将该程序集顺利的安装到全局程序集缓存。命令如下所示:
// -Vr开关会将程序集的身份添加到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification注册表子项中
SN.exe -Vr MyAssembly.dll
3.打包和部署程序集时使用SN.exe工具的-Ra开关让签名文件的私钥对程序集进行签名并将RSA数字签名存储到PE文件中预留的空间。命令如下所示:
// 使用MyCompany.PrivateKey私钥文件对程序集MyAssembly.dll进行签名并将RSA数字签名存储到PE文件中预留的空间
SN.exe -Ra MyAssembly.dll MyCompany.PrivateKey
4.在实际环境中测试时使用SN.exe工具的-Vu或者-Vx开关重新启用对程序集的校验。命令如下所示:
SN -Vu MyAssembly.dll
5.当签名文件在CSP容器中时,就要将上述的/keyfile换成/keycontainer,-Ra换成-Rc。
程序集部署区别:程序集可以采取私有或全局两种部署方式。其中私有部署指的是部署到应用程序根目录或者子目录;而全局部署指的是部署到全局程序集缓存。由于全局部署没有对注册表进行操作,所以没法实现简单的安装,备份,还原,移动和删除应用程序,所以我们建议尽量使用私有而不是全局部署。
弱命名和强命名程序集区别:如下所示:
1.两种程序集的结构完全相同,唯一区别就是强命名程序集使用发布者的秘钥进行签名。
2.弱命名程序集只能使用私有部署方式,而强命名程序集可以使用私有和全局两种部署方式。
3.CLR查找弱命名程序集时会根据程序集名称(包含扩展名)从部署的位置处进行查找。
4.CLR查找强命名程序集时会根据程序集名称(不包含扩展名),版本号,语言文化和公钥从部署的位置处进行查找。
运行时解析类型引用:CLR在解析类型引用时可以从相同文件,不同文件相同程序集,不同文件不同程序集三个地方进行查找。如果出现找不到程序集或者无法加载程序集或者程序集被篡改等情况时就会抛出异常。如果不抛出异常继续运行的话,就需要向System.AppDomain的AssemblyResolve,ReflectionOnly-AssemblyResolve和TypeResolve事件注册回调方法,在回调方法中执行解决异常问题的代码。解析流程如图所示:
应用程序配置文件:CLR在应用程序配置文件中进行检查程序集或者版本号以及版本号重定向操作。常见的元素如下所示:
1.codeBase元素:查找弱命名程序集时,不能有version属性且url必须指向应用程序根目录下的一个子目录;查找强命名程序集时,会尝试从url处查找。
2.probing元素:查找弱命名程序集时,检查应用程序根目录下的privatePath指定的子目录;查找强命名程序集时,检查全局程序集缓存或者由codeBase元素指定的url,只有在未指定codeBase元素时才会检查应用程序根目录下的privatePath指定的子目录。
3.assemblyIdentify元素:指定程序集的名称,公钥标记和语言文化等。
4.bindingRedirect元素:将旧区间版本号重定向成新版本号。
5.publisherPolicy元素:是否应用发布者策略文件。当为应用时,CLR会在全局程序集缓存中检查新的程序集版本,并进行程序集发布者认为有必要的任何版本号的重定向操作。
发布者策略控制:在发布新程序集给用户安装时,通过新程序集中的策略控制文件来将指定版本号的旧程序集自动更新成新版本程序集。具有以下特性:
1.发布者策略程序集必须安装到全局程序集缓存中。
2.发布者策略控制文件的元素除了不能使用publisherPolicy和probing外,其余可以使用的元素跟应用程序配置文件一样。
3.发布者策略程序集的名称格式为"Policy.旧主版本号.旧次版本号.旧程序集名.新程序集扩展名"。
4.发布者策略程序集含有自身的版本号,CLR会查找最新版本号的发布者策略程序集。
5.发布者策略程序集必须跟旧程序集使用相同的签名文件,只有这样才能判定是同一个程序集。
6.生成发布者策略程序集时必须使用/linkresource开关来将发布者策略控制文件以一个单独的XML文件链接到程序集内,而不是使用/embedresource开关来将发布者策略控制文件嵌入到程序集内。