两种程序集,两种部署
CLR支持两种类型的程序集:弱命名程序集(weakly named assembly)和强命名程序集(strongly named assembly)。二者的区别:强命名程序集使用发布者的公钥/私钥进行了签名。这一堆密钥允许对程序集进行唯一性的标识、保护和版本控制。
程序集可采用两种方式部署:私有或全局。私有部署的程序集是指部署到应用程序基目录或子目录的程序集,弱命名程序集只能以私有方式部署。全局部署的程序集是指部署到一些公认位置的程序集(GAC),CLR在查找程序集时,会检查这些位置,强命名程序集既可以私有部署,也可以全局部署。
为程序集分配强名称
强命名程序集有4个重要特性:文件名、版本号、语言文化和公钥(由于公钥数字很大,所以通常使用从公钥派生的小哈希值,称为公钥标记)。以下是一个强命名程序集标识字符串及说明:
如何使用Visual Studio创建密钥文件以及强命名程序集这里就不介绍了,Properties|Signing|Sign the assembly,下面介绍使用强命名工具sn.exe以及C#命令行编译器csc.exe生成强命名程序集
创建强命名程序集
-
使用强命名工具sn.exe创建密钥文件。
sn.exe位置: C:Program Files (x86)Microsoft SDKsWindowsv10.0AinNETFX 4.6.1 Tools
使用如下命令,创建密钥文件:
SN -k Answer.snk
命令执行完毕后会在当前目录生成Answer.snk文件。
注:必须以管理员身份打开命令行程序cmd.exe,否则无法创建密钥文件。
如果要查看该密钥文件的实际公钥,执行以下两条命令:
SN -p Answer.snk Answer.PublicKey sha256 使用-p命令创建只含公钥的文件
SN -tp Answer.PublicKey 使用-tp命令显示公钥标记
-
使用密钥文件创建强命名程序集
本节沿用上一篇中创建的CommonTypes.cs文件,将步骤1中生成的Answer.snk文件拷贝到D: est目录下,执行以下命令:
C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe /t:library /keyfile:Answer.snk CommonTypes.cs
C#编译器打开指定密钥文件(Answer.snk),用私钥对程序集进行签名,并将公钥嵌入清单中。生成强命名程序集时,程序集的FileDef清单元数据表列出构成程序集的所有文件,每将一个文件名添加到清单,都对文件内容进行哈希处理,哈希值和文件名一同被存入FileDef表中。使用ILDasm.exe工具查看程序集元数据,可以看到公钥已经被嵌入程序集清单中。
##全局程序集缓存
创建了强命名程序集之后,我们就可以部署它了。文章的开头已经介绍,全局部署的程序集是指部署到一些公认位置的程序集,这个公认位置就是全局程序集缓存(Global Assembly Cache,GAC)。GAC位置如下:
C:WindowsMicrosoft.NETassembly
在GAC中安装或卸载强命名程序集最常用的工具是GACUtil.exe。
GACUtil.exe位置:
C:Program Files (x86)Microsoft SDKsWindowsv10.0AinNETFX 4.6.1 Tools
将上一节中创建的CommonTypes.dll注册到GAC中,执行语句:
gacutil.exe /i D: estCommonTypes.dll
提示注册成功后,路径C:WindowsMicrosoft.NETassemblyGAC_MSIL下生成CommonTypes文件夹。
##强命名程序集的引用
任何程序集都包含对其他强命名程序集的引用,因为System.Object定义在MSCorLib.dll中,MSCorLib.dll就是强命名程序集。使用csc.exe的/reference编译器开关指定想引用的程序集文件时,若提供完整路径,csc.exe会加载指定文件,并根据它的元数据生成程序集;若指定不包含路径的文件名,csc.exe会在如下目录查找程序集:
- 工作目录
- csc.exe所在目录,目录中还包含CLR的各种DLL文件
- 使用/lib编译器开关指定的任何目录
- 使用LIB环境变量指定的任何目录
虽然编译时会在这里查找程序集,但运行时不会在这里加载程序集。安装.NET Framework时,实际会安装两套程序集文件。一套在CLR目录,另一套在GAC的子目录。CLR目录下的文件便于生成程序集,而GAC中的文件便于运行时加载。
强命名程序集防篡改
用私钥对程序集进行签名,并将公钥嵌入程序集中,CLR就可以验证程序集是否被修改或破坏。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,将哈希值与PE文件中嵌入的RSA数字签名进行比较,值完全相同则表明文件内容未被篡改。此外,系统还会对程序集的其他内容进行哈希处理,并与清单文件的FileDef表进行比较。
延迟签名
只有少数人才能访问私钥,而开发和测试的过程中,需要经常对程序集进行修改,.NET Framework提供了延迟签名技术。延迟签名允许只使用公钥生成程序集,暂不用私钥,等到打包和部署程序集时再进行签名。下面总结了使用延迟签名技术开发程序集的步骤:
-
新建DelaySignSample.cs文件
public class DelaySignSample { static void Main() { System.Console.WriteLine("Hello"); System.Console.ReadLine(); } }
-
使用公钥文件编译程序集
csc /t:library /keyfile:Answer.PublicKey /delaysign DelaySignSample.cs
-
指定-Vr命令行开关,使CLR暂时信任程序集内容,不进行哈希建检验(这使得程序集能顺利安装到GAC,否则注册时会报错)
SN.exe -Vr DelaySignSample.dll
-
项目开发完成后,使用私钥进行签名
SN -R DelaySignSample.dll Answer.snk
-
重新启用对该程序集的验证
SN -Vu DelaySignSample.dll
##运行时解析类型引用
运行应用程序时,CLR会加载并初始化自身,读取程序集的CLR头,查找标识了应用程序入口方法(Main)的MethodDefToken,检索MethodDef源数据表找到IL代码在文件中的偏移量,将IL代码JIT编译成本机代码,最后执行本机代码。
具体的执行过程在《CLR的执行模型》中已经详细介绍。
下图展示了CLR加载程序集并扫描其元数据来定位类型的过程: