CLR via C#, 4th -- 【CLR基础】 -- 第3章共享程序集和强命名程序集

3.1 两种程序集,两种部署

CLR支持两种程序集:弱命名程序集(weakly named assembly)和强命名程序集(strongly named assembly)。

(任何文档都找不到“弱命名程序集”这个术语,这是我自创的。)

弱命名和强命名程序集结构完全相同:PE32(+)头、CLR头、元数据、清单表以及IL.

两者真正的区别在于,强命名程序集使用发布者的公钥/私钥进行了签名。

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

3.2 为程序集分配强名称

要由多个应用程序访问的程序集必须放到公认的目录,只根据文件名来区分程序集明显不够。
CLR必须支持对程序集进行唯一性标识的机制。这就是所谓的“强命名程序集”。

强命名程序集具有4个重要特性,它们共同对程序集进行唯一性标识:文件名(不计扩展名)、版本号、语言文化和公钥。由于公钥数字很大,所以经常使用从公钥派生的小哈希值,称为公钥标记(public key token)。

由于弱命名程序集总是私有部署,所以CLR在应用程序基目录或子目录中搜索程序集文件时只使用程序集名称(添加.dl或exe扩展名)。

创建强命名程序集是用.NET Framework SDK和Microsoft Visual Studio随带的Strong Name实用程序(SN.exe)获取密钥。

SN –k MyCompany.snk
SN –p MyCompany.snk MyCompany.PublicKey sha256
SN –tp MyCompany.PublicKey

编译程序集时使用/keyfile:<file>编译器开关

csc /keyfile:MyCompany.snk Program.cs

C#编译器看到这个开关会打开指定文件(MyCompany.snk),用私钥对程序集进行签名,并将公钥嵌入清单。注意只能对含清单的程序集文件进行签名;程序集其他文件不能被显式签名。
要在Visual Studio中新建公钥/私钥文件,可显示项目属性,点击“签名”标签,勾选“为程序集签名”,然后从“选择强名称密钥文件”选择框中选择“<新建…"
“对文件进行签名”的准确含义是:生成强命名程序集时,程序集的FileDef清单元数据表列出构成程序集的所有文件。每将一个文件名添加到清单,都对文件内容进行哈希处理。

注意,CLR在做出安全或信任决策时,永远都不会使用公钥标记,因为几个公钥可能在哈希处理之后得到同一个公钥标记。

3.3 全局程序集缓存

由多个应用程序访问的程序集必须放到公认的目录,而且CLR在检测到对该程序集的引用时,必须知道检查该目录。这个公认位置就是全局程序集缓存(Global Assembly Cache,GAC),GAC的具体位置不同版本会有所变化。一般能在以下目录发现它:

%SystenRoot%\Microsoft.NET\Assembly

GAC目录是结构化的:其中包含许多子目录,子目录名称用算法生成。永远不要将程序集文件手动复制到GAC目录;相反,要用工具完成这项任务。工具知道GAC的内部结构,并知道如何生成正确的子目录名。

开发和测试时在GAC中安装强命名程序集最常用的工具是GACUtil.exe

使用GACUtil.exe的/i开关将程序集安装到GAC,/u开关从GAC卸载程序集。注意不能将弱命名程序集放到GAC。

GACUtilexe 的/开关方便开发人员在测试时使用。但如果是在生产环境中部署,建议安装或卸载程序集时除了指定/i/u开关,还要指定/r开关。/r开关将程序集与Windows的安装与卸载引擎集成。简单地说,它告诉系统哪个应用程序需要程序集,并将应用程序与程序集绑定。

为什么要在GAC中“注册”程序集?

假定两家公司都生成了名为OurLibrary的程序集,两个程序集都由一个OurlLibrary.dll文件构成。这两个文件显然不能存储到同一个目录,否则最后一个安装的会覆盖第一个,造成应用程序被破坏。相反,将程序集安装到GAC,就会在%SystemRoot%\Microsoft.NET\Assembly目录下创建专门的子目录,程序集文件会复制到其中一个子目录。

3.4 在生成的程序集中引用强命名程序集

你生成的任何程序集都包含对其他强命名程序集的引用,这是因为System.Object在MSCorLib.dll中定义,后者就是强命名程序集。

使用CSC.exe的/reference编译器开关指定想引用的程序集文件名。

CSC.exe会尝试在以下目录查找程序集(按所列顺序)。
1,工作目录。
2,CSC.exe所在的目录,目录中还包含CLR的各种DLL文件。
3,使用/lib编译器开关指定的任何目录。
4,使用LIB环境变量指定的任何目录。

安装.NET Framework时,实际会安装Microsoft的程序集文件的两套拷贝。一套安装到编译器/CLR目录,另一套安装到GAC的子目录。编译器/CLR目录中的文件方便你生成程序集,而GAC中的拷贝则方便在运行时加载。
CSC.exe编译器之所以不在GAC中查找引用的程序集,是因为你必须知道程序集路径,而GAC的结构又没有正式公开。第二个方案是让CSC.exe允许你指定一个依然很长但相对比较容易阅读的字符串,比如"System.Drawing,Version=v4.0.0.0,Culture-neutral,PublickeyToken-b03517f11d50a3a"。但这两个方案都不如在用户硬盘上安装两套一样的程序集文件。

3.5 强命名程序集能防篡改

用私钥对程序集进行签名,并将公钥和签名嵌入程序集,CLR就可验证程序集未被修改或破坏。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,将哈希值与PE文件中嵌入的RSA数字签名进行比较(在用公钥解除了签名之后)。如果两个值完全一致,表明文件内容未被篡改。此外,系统还对程序集的其他文件的内容进行哈希处理,并将哈希值与清单文件的FileDef表中存储的哈希值进行比较。任何一个哈希值不匹配,表明程序集至少有一个文件被篡改,程序集将无法安装到GAC.

3.6 延迟签名

延迟签名(delayed signing)也称为部分签名(partial signing),延迟签名允许只用公司的公钥生成程序集,暂时不用私钥。

进行延迟签名,文件内容不会在这个时候进行哈希处理。

如果是C#编译器,就指定/delaysign编译器开关。如果是Visual Studio,就打开项目属性页,在“签名”选项卡中勾选“仅延迟签名”。如果使用AL.exe,就指定/delay[sign]命令行开关。

csc /keyfile:MyCompany.PublicKey /delaysign MyAssembly.cs
SN.exe –Vr MyAssembly.dll
SN.exe ­Ra MyAssembly.dll MyCompany.PrivateKey
SN.exe –Vu MyAssembly.dll

3.7私有部署强命名程序集

私有部署达成了“简单复制部署”(简单复制一下程序集的文件就可以完成部署。)目标,而且能更好地隔离应用程序及其程序集。

3.8“运行时”如何解析类型引用

解析引用的类型时,CLR可能在以下三个地方找到类型:

相同文件

编译时便能发现对相同文件中的类型的访问,这称为早期绑定(early binding)。类型直接从文件中加载,执行继续。

对应地,在运行时通过反射机制绑定到类型并调用方法,就称为晚期绑定(late binding)。

不同文件,相同程序集

“运行时”确保被引用的文件在当前程序集元数据的FileDef表中,检查加载程序集清单文件的目录,加载被引用的文件,检查哈希值以确保文件完整性。发现类型的成员,执行继续。

不同文件,不同程序集

如果引用的类型在其他程序集的文件中,“运行时”会加载被引用程序集的清单文件。如果需要的类型不在该文件中,就继续加载包含了类型的文件。发现类型的成员,执行继续。

注意 ModuleDef,ModuleRef和FileDef元数据表在引用文件时使用了文件名和扩展名。但AssemblyRef元数据表只使用文件名,无扩展名。

3.9高级管理控制(配置)

<?xml version="1.0"?>  
<configuration>   
   <runtime>   
      <assemblyBinding xmlns="urn:schemas ­microsoft­com:asm.v1">   
         <probing privatePath="AuxFiles;bin\subdir" />  
         <dependentAssembly>  
            <assemblyIdentity name="SomeClassLibrary"   
              publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/>   
            <bindingRedirect   
              oldVersion="1.0.0.0" newVersion="2.0.0.0" />  
            <codeBase version="2.0.0.0"    
              href="http://www.Wintellect.com/SomeClassLibrary.dll" />   
         </dependentAssembly>  
         <dependentAssembly>  
            <assemblyIdentity name="TypeLib"    
              publicKeyToken="1f2e74e897abbcfe" culture="neutral"/>   
            <bindingRedirect   
              oldVersion="3.0.0.0 ­3.5.0.0" newVersion="4.0.0.0" />   
            <publisherPolicy apply="no" />   
         </dependentAssembly>  
      </assemblyBinding>  
   </runtime>  
</configuration>

probing元素
查找弱命名程序集时,检查应用程序基目录下的AuxFiles和binsubdir子目录。对于强命名程序集,CLR检查GAC或者由codeBase元素指定的URL。只有在未指定codeBase元素时,CLR才会在应用程序的私有路径中检查强命名程序集。
第一个dependentAssembly,assemblyldentity和bindingRedirect元素

查找由控制着公钥标记32ab4ba45e0a69al的组织发布的、语言文化为中性的SomeClassLibrary程序集的1.0.0.0版本时,改为定位同一个程序集的2.0.0.0版本。
codeBase元素
查找由控制着公钥标记32ab4ba45e0a69a1的组织发布的、语言文化为中性的SomeClassLibrary程序集的2.0.0.0版本时,尝试在以下URL处发现它:www.Wintellect.com/SomeClassLibrary.dll,虽然第2章没有特别指出,但codeBase元素也能用于弱命名程序集。如果是这样,程序集版本号会被忽略,而且根本就不应该在XML codeBase元素中写这个版本号。另外,codeBase定义的URL必须指向应用程序基目录下的一个子目录。
第二个dependentAssembly,assemblyldentity和bindingRedirect元素

查找由控制着公钥标记12e74e897abbcfe的组织发布的、语言文化为中性的TypeLib程序集的3.0.0.0到3.5.0.0版本时(包括3.0.0.0和3.5.0.0在内),改为定位同一个程序集的4.0.0.0版本。
publisherPolicy元素
如果生成TypeLib程序集的组织部署了发布者策略文件(详情在下一节讲述),CLR应忽略该文件。

发布者策略控制

<configuration>   
   <runtime>   
      <assemblyBinding xmlns="urn:schemas ­microsoft­com:asm.v1">   
         <dependentAssembly>  
            <assemblyIdentity name="SomeClassLibrary"   
              publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/>   
            <bindingRedirect   
              oldVersion="1.0.0.0" newVersion="2.0.0.0" />  
            <codeBase version="2.0.0.0"    
              href="http://www.Wintellect.com/SomeClassLibrary.dll"/>  
         </dependentAssembly>  
      </assemblyBinding>  
   </runtime>  
</configuration>

该配置文件告诉CLR一旦发现对SomeClassLibrary程序集的1.0.0.0版本的引用,就自动加载2.0.0版本。现在,发布者就可以创建包含该发布者策略配置文件的程序集,像下面这样运行AL.exe:

AL.exe /out:Policy.1.0.SomeClassLibrary.dll   
       /version:1.0.0.0   
       /keyfile:MyCompany.snk  
       /linkresource:SomeClassLibrary.config

关于发布者策略最后注意一点。假定发布者推出发布者策略程序集时,因为某种原因,新程序集引入的bug比它能修复的bug还要多,那么管理员可指示CLR忽略发布者策略程序集。这要求编辑应用程序的配置文件并添加以下publisherPolicy元素:

<publisherPolicy apply="no"/>

 

posted @ 2019-10-21 11:34  FH1004322  阅读(147)  评论(0)    收藏  举报