共享程序集和强命名程序集

两种程序集,两种部署


  CLR支持两种类型的程序集:弱命名程序集(weakly named assembly)和强命名程序集(strongly named assembly)。二者的区别:强命名程序集使用发布者的公钥/私钥进行了签名。这一堆密钥允许对程序集进行唯一性的标识、保护和版本控制。

  程序集可采用两种方式部署:私有或全局。私有部署的程序集是指部署到应用程序基目录或子目录的程序集,弱命名程序集只能以私有方式部署。全局部署的程序集是指部署到一些公认位置的程序集(GAC),CLR在查找程序集时,会检查这些位置,强命名程序集既可以私有部署,也可以全局部署。

为程序集分配强名称


  强命名程序集有4个重要特性:文件名、版本号、语言文化和公钥(由于公钥数字很大,所以通常使用从公钥派生的小哈希值,称为公钥标记)。以下是一个强命名程序集标识字符串及说明:

如何使用Visual Studio创建密钥文件以及强命名程序集这里就不介绍了,Properties|Signing|Sign the assembly,下面介绍使用强命名工具sn.exe以及C#命令行编译器csc.exe生成强命名程序集

创建强命名程序集

  1. 使用强命名工具sn.exe创建密钥文件。

    sn.exe位置:
    C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 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命令显示公钥标记

  2. 使用密钥文件创建强命名程序集

    本节沿用上一篇中创建的CommonTypes.cs文件,将步骤1中生成的Answer.snk文件拷贝到D:\test目录下,执行以下命令:

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /t:library /keyfile:Answer.snk CommonTypes.cs

    C#编译器打开指定密钥文件(Answer.snk),用私钥对程序集进行签名,并将公钥嵌入清单中。生成强命名程序集时,程序集的FileDef清单元数据表列出构成程序集的所有文件,每将一个文件名添加到清单,都对文件内容进行哈希处理,哈希值和文件名一同被存入FileDef表中。使用ILDasm.exe工具查看程序集元数据,可以看到公钥已经被嵌入程序集清单中。


全局程序集缓存


  创建了强命名程序集之后,我们就可以部署它了。文章的开头已经介绍,全局部署的程序集是指部署到一些公认位置的程序集,这个公认位置就是全局程序集缓存(Global Assembly Cache,GAC)。GAC位置如下:

C:\Windows\Microsoft.NET\assembly

在GAC中安装或卸载强命名程序集最常用的工具是GACUtil.exe。

GACUtil.exe位置:
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools

将上一节中创建的CommonTypes.dll注册到GAC中,执行语句:

gacutil.exe /i D:\test\CommonTypes.dll

提示注册成功后,路径C:\Windows\Microsoft.NET\assembly\GAC_MSIL下生成CommonTypes文件夹。


强命名程序集的引用


  任何程序集都包含对其他强命名程序集的引用,因为System.Object定义在MSCorLib.dll中,MSCorLib.dll就是强命名程序集。使用csc.exe的/reference编译器开关指定想引用的程序集文件时,若提供完整路径,csc.exe会加载指定文件,并根据它的元数据生成程序集;若指定不包含路径的文件名,csc.exe会在如下目录查找程序集:

  1. 工作目录
  2. csc.exe所在目录,目录中还包含CLR的各种DLL文件
  3. 使用/lib编译器开关指定的任何目录
  4. 使用LIB环境变量指定的任何目录

虽然编译时会在这里查找程序集,但运行时不会在这里加载程序集。安装.NET Framework时,实际会安装两套程序集文件。一套在CLR目录,另一套在GAC的子目录。CLR目录下的文件便于生成程序集,而GAC中的文件便于运行时加载。

强命名程序集防篡改


  用私钥对程序集进行签名,并将公钥嵌入程序集中,CLR就可以验证程序集是否被修改或破坏。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,将哈希值与PE文件中嵌入的RSA数字签名进行比较,值完全相同则表明文件内容未被篡改。此外,系统还会对程序集的其他内容进行哈希处理,并与清单文件的FileDef表进行比较。

延迟签名


  只有少数人才能访问私钥,而开发和测试的过程中,需要经常对程序集进行修改,.NET Framework提供了延迟签名技术。延迟签名允许只使用公钥生成程序集,暂不用私钥,等到打包和部署程序集时再进行签名。下面总结了使用延迟签名技术开发程序集的步骤:

  1. 新建DelaySignSample.cs文件

    public class DelaySignSample
    {
        static void Main()
        {
            System.Console.WriteLine("Hello");
            System.Console.ReadLine(); 
        }
    }
  2. 使用公钥文件编译程序集

    csc /t:library /keyfile:Answer.PublicKey /delaysign DelaySignSample.cs
  3. 指定-Vr命令行开关,使CLR暂时信任程序集内容,不进行哈希建检验(这使得程序集能顺利安装到GAC,否则注册时会报错)

    SN.exe -Vr DelaySignSample.dll
  4. 项目开发完成后,使用私钥进行签名

    SN -R DelaySignSample.dll Answer.snk
  5. 重新启用对该程序集的验证

    SN -Vu DelaySignSample.dll


运行时解析类型引用


  运行应用程序时,CLR会加载并初始化自身,读取程序集的CLR头,查找标识了应用程序入口方法(Main)的MethodDefToken,检索MethodDef源数据表找到IL代码在文件中的偏移量,将IL代码JIT编译成本机代码,最后执行本机代码。

具体的执行过程在《CLR的执行模型》中已经详细介绍。

  下图展示了CLR加载程序集并扫描其元数据来定位类型的过程:

posted @ 2017-08-12 16:43 Answer.Geng 阅读(...) 评论(...) 编辑 收藏