CLR via C# 边读边想 03 - 本地程序集和强命名程序集
2012-06-25 20:38 richardzhaoxb 阅读(215) 评论(0) 编辑 收藏 举报Two Kinds of Assemblies, Two Kinds of Deployment
CLR 支持两种类型的程序集:weakly named assemblies and strongly named assemblies。这两种类型的Assembly在文件内部结构上没有什么区别,都是按照前面两章介绍的由 PE32(+) header,CLR header,metadata,manifest table,还有 IL 组成。而且也是用同样的工具来生成程序集,例如 CSC.exe 和 AL.exe 。
他们最大的区别就是强命名的程序集会由发布者的一对key来为其签名,而这对key可以唯一的标识这个发布者。这对key也让程序集成为了唯一标识的、安全的、有明确版本的程序集。
程序集有两种部署的方式:本地的和全局的。本地部署就是将程序集拷贝到程序的目录或子目录。weakly named assemblies 只能被本地部署。而 strongly named assemblies 既可以被本地部署也可以被全局部署。
注意:强烈建议使用 strongly named assemblies,很有可能在以后的 CLR 中要求所有的 assembly 都是 请强命名的。 weakly named assembly 将不再被鼓励使用。
Giving an Assembly a Strong Name
一个强命名由四部分组成:
- 文件名,不带扩展名的。
- 版本号,在 manifest table 中定义
- 文化表示,在 manifest table 中定义
- 公钥。 由于一个公钥通常是一个非常大的数字,所以一般使用从这个公钥经过一个哈希算法得到一个 public key token。
那么如何确保区分不同公司发布的 assembly 是不同的呢? 微软选择的是标准的 公钥/私钥 加密技术,这对key可以确保两个公司就算发布了一样名字和版本的程序集,也不会产生混淆。
在上一章节中,我们知道可以给程序集设置版本号和culture,这些信息存储在 manifest metadata 中。然而,对于 weakly name assembly 来说,CLR 会忽略 version number,只关注 assembly name。
创建一个强命名的程序集的第一步是用 Strong Name 工具获得一个key,用下面的命令行可以产生一对key:
SN –k MyCompany.snk
注意: SN.exe 命令的选项是区分大小写的。
上面的命令产生了一个名叫 MyCompany.snk 的文件,它包含了一对以二进制存在的公钥和私钥的 key number。
公钥 key number 是非常大的一个数,如果你想看看这个公钥 key number,用下面的方法:
第一步,把公钥单独生成一个文件:
SN –p MyCompany.snk MyCompany.PublicKey
第二步,打印出公钥 key number:
SN –tp MyCompany.PublicKey
SN.exe 工具不提供任何方法显示私钥。
由于公钥太大,不方便使用,所以使用更加简单的 public key token ,这个token是从 public key 得到的一个 64-bit 的哈希值的最后8个字节。
在build程序时使用强命名的方法:
csc /keyfile:MyCompany.snk Program.cs
上面的命令,编译器会使用 MyCompany.snk 中的 private key 来给程序集签名,而后把 public key 签入到 manifest 中,注意,你只给包含 manifest 的文件签名,其他的文件没有被签名。
默认情况下,使用的是SHA-1算法,你可以用 AL.exe’s /algid 选项来切换算法。 但是SHA-1算法已经满足了绝大多数的应用。
签名后的结构如下图,PE的所有内容都被hash之后,又被发布者的 private key 签名,得到一个 RSA 的数字签名,这个数字签名又被签入到 PE 文件中,CLR header 会被更新记录下 数字签名在 PE 文件中的位置。
由 file name, the assembly version, the culture, and the public key 这四部分组成的强命名,让程序集成为唯一的,不用担心不同公司会产生相同强命名的程序集。除非这个公司把他的公钥/私钥对和别人共享了。
The GAC (Global Assembly Cache)
之前介绍了如何创建强命名的程序集,现在来看看如何部署强命名的程序集,以及CLR如何找到和加载它。
当CLR要加载一个强命名的程序集时,它会到GAC中去找。 GAC 的地址在 C:\Windows\Assembly
GAC 目录中包含有很多子目录, 有一个特殊的规则来命名这些子目录。所以千万不要自己往这个目录/子目录下拷贝文件,而是应该用专门的工具(gacutil.exe)来做部署。
/i 选项用来向GAC中安装。/u 选项用来从GAC中卸载。
注意:运行 gacutil.exe 需要 Windows Administrator group 的权限。
Building an Assembly That References a Strongly Named Assembly
不论你开发任何程序,你都会引用到强命名的程序集,因为System.Object定义在MSCorLib.dll中,这个dll 是强命名的。
在使用 CSC.exe 时,如果在 /reference 后面跟的是一个全路径,编译器会加载一个 local assembly,如果不是一个全路径,编译器会按照顺序依次到下面的几个目录寻找:
- working directory,当前目录
- csc.exe 所在的目录
- /lib 的选项后面指定的路径,如果有 /lib 选项的话。
- LIB环境变量指定的路径,如果有 LIB 环境变量的话。
举个例子,如果你在编译时加了一个选项:/reference:System.Drawing.dll,编译器会到上述的几个目录去找这个dll,会在第2点,csc.exe 所在目录找到。但是要特别注意,虽然编译时引用的是 csc.exe 目录下的文件,但是在 runtime ,引用的却不是这个目录下的文件。
你可以发现,安装了.Net Framework 后,微软只带的 Assembly 分别在安装目录和GAC中拷贝了一份,安装目录中主要用来编译时被引用,而GAC中的是在 runtime 被引用。
Strongly Named Assemblies Are Tamper-Resistant
强命名如何能起到防篡改的功能呢? 当一个 assembly 被安装到 GAC,系统把包含 manifest 的文件取 hash 值,并和嵌入在 PE 中的 RSA 数字签名比较,如果相同说明是没有被篡改过的,如果不同,说明这个 assembly 被发布者之外的人篡改过,也就不能被安装到GAC中。
Delayed Signing
如果你要发布强命名的程序集,你需要用到私钥来签名。然而,在开发和测试时,你不能方便的访问到私钥,正应为如此,微软支持延迟签名,也被称为部分签名。延迟签名就是允许你先用公钥签名。由于可以访问到公钥,那么其他引用这个延迟签名的assembly的其他程序集就可以嵌入一个正确的public key在 AssemblyRef manifest中。延迟签名的程序集也可以放在GAC中。 但是不能防止被篡改。
使用 /delaysign 选项告诉编译器做延迟签名。使用 延迟签名做开发和部署的整个过程如下:
1.先获得public key,然后使用下面的命令行
csc /keyfile:MyCompany.PublicKey /delaysign MyAssembly.cs
2.运行下面的命令行,让CLR不要对程序集的内容做hash和比较,这样可以允许把延迟签名的程序集按照到GAC。这样你就可以对你的程序集进行测试。
SN.exe –Vr MyAssembly.dll
3.当准备好要发布时,获取公司的私钥, 执行下面的命令。
SN.exe -R MyAssembly.dll MyCompany.PrivateKey
但是在安装到GAC前,请先执行下面的步骤。
4.把 verification 在开启,使用下面的命令:
SN –Vu MyAssembly.dll
Privately Deploying Strongly Named Assemblies
虽然强命名的程序集可以被安装到GAC,但是事实上,我们只有在程序集会被多个App引用时才有必要把它放到GAC中。