强命名程序集

GAC

强名的作用是识别篡改、防止dll冲突。

  GAC中的所有的Assembly都会存放在系统目录"%winroot%/assembly下面。放在系统目录下的好处之一是可以让系统管理员通过用户权限来控制Assembly的访问。

  关于GAC本身,GAC全称是Global Assembly Cache,他的作用是可以存放一些有很多程序都要用到的公共Assembly,例如System.DataSystem.Windows.Forms等等。这样,很多程序就可以从GAC里面取得Assembly,而不需要再把所有要用到的Assembly都拷贝到应用程序的执行目录下面。举例而言,如果没有GAC,那么势必每个WinForm程序的目录下就都要从C:/WINDOWS/Microsoft.NET/Framework/v1.0.3705下面拷贝一份System.Windows.Forms.dll,这样显然不如都从GAC里面取用方便,也有利于Assembly的升级和版本控制。

  除了系统默认放置在GAC中的AssemblySystem.Windows.Forms以外,我们也可以添加自己的Assembly

1)         创建一个strong-nameAssembly,例如ToolbarComponent.dll  

2)         运行gacutil   -i   ToolbarComponent.dll,把这个Assembly添加到GAC  

3)         在程序中动态装载:

System.Reflection.Assembly ass=Assembly.Load("ToolbarComponent,Version=1.0.934.20434,Culture=neutral,PublicKeyToken=65f45658c8d4927f");  

MessageBox.Show("Is the assembly loaded from GAC?"+ass.GlobalAssemblyCache);

  在上面的程序中,ToolbarComponent就是从GAC装载而不是从程序的运行目录下的dll文件中装载,程序目录下不需要放置ToolbarComponent.dll程序也能正常运行。另外,Assembly.Load()中的参数可以通过"gacutil   -l"查到。

  另外,上面提到了GAC中的Assembly必须是strong-name的。创建strong-nameAssembly的步骤大致如下:  

a)         在命令行运行“sn -k keyPair.snk”创建一个密钥文件。这里的sn.exe也是.NET附带的一个工具。  

b)         VS.NET里面修改“AssemblyInfo.cs”文件:  

[assembly: AssemblyDelaySign(false)]    

[assembly: AssemblyKeyFile("..//..//keyPair.snk")]    

c)         编译项目,就能得到一个strong-nameAssembly  

MSDN中有一些对GAC的介绍,您可以参考:  

1)         Assembly Cache Viewer (Shfusion.dll)  

2)         Global Assembly Cache  

3)         .NET Framework中附带了一些和GAC有关的工具,其中包括:  

1Gacutil.exe,一个命令行的工具,用于在GAC中浏览、添加、删除Assembly  

2Ngen.exe,也是一个命令行的工具,用于在GAC中创建Native   Image  

3mscorcfg.msc,一个MMC终端,可以图形化完成Gacutil.exe的主要功能。

强命名程序集(Strong Name Assembly)的概念

  因为不同的公司可能会开发出有相同名字的程序集来,如果这些程序集都被复制到同一个相同的目录下,最后一个安装的程序集将会代替前面的程序集。这就是著名的Windows “DLL Hell”出现的原因。

  很明显,简单的用文件名来区分程序集是不够的,CLR需要支持某种机制来唯一的标识一个程序集。这就是所谓的强命名程序集。

  一个强命名程序集包含四个唯一标志程序集的特性:文件名(没有扩展名),版本号,语言文化信息(如果有的话),公有秘钥。

  这些信息存储在程序集的清单(manifest)中。清单包含了程序集的元数据,并嵌入在程序集的某个文件中。

  下面的字符串标识了四个不同的程序集文件:

“MyType, Version=1.0.1.0,

Culture=neutral, PublicKeyToken=bf5779af662fc055”

“MyType, Version=1.0.1.0,

Culture=en-us, PublicKeyToken=bf5779af662fc055”

“MyType, Version=1.0.2.0,

Culture=neturl, PublicKeyToken=bf5779af662fc055”

“MyType, Version=1.0.2.0,

Culture=neutral, PublicKeyToken=dbe4120289f9fd8a”

  如果一个公司想唯一的标识它的程序集,那么它必须首先获取一个公钥/私钥对,然后将共有秘钥和程序集相关联。不存在两个两个公司有同样的公钥/私钥对的情况,正是这种区分使得我们可以创建有着相同名称,版本和语言文化信息的程序集,而不引起任何冲突。

  与强命名程序集对应的就是所谓的弱命名程序集。(其实就是普通的没有被强命名的程序集)。两种程序集在结构上是相同的。都使用相同的PE文件格式,PE表头,CLR表头,元数据,以及清单(manifest)。二者之间真正的区别在于:强命名程序集有一个发布者的公钥/私钥对签名,其中的公钥/私钥对唯一的标识了程序集的发布者。利用公钥/私钥对,我们可以对程序集进行唯一性识别、实施安全策略和版本控制策略,这种唯一标识程序集的能力使得应用程序在试图绑定一个强命名程序集时,CLR能够实施某些已确知安全的策略(比如只信任某个公司的程序集)。

如何部署强命名程序集(Strong Name Assembly)和GAC

1.       如何创建强命名程序集(Strong Name Assembly

创建一个强命名程序集首先需要获得一个用强命名实用工具

Strong Name Utility,即SN.exe.NET SDK自带)产生的密钥。

下面简要介绍一下SN.exe的一些用法。要产生一个公钥/私钥对:

a)       SN –k MyCompany.Keys

该命名告诉SN.exe创建一个名为MyCompany.keys的文件。MyCompany.keys文件将包含以对以二进制格式存储的公有密钥和私有密钥。

b)       查看公有密钥:

首先生成一个只包含公有密钥的文件: SN –p

MyCompany.keys MyCompany.PublicKey

然后用-tp参数查看:SN –tp MyCompany.PublicKeys

Public key is

00240000048000009400000006020000002400005253413

10004000001000100bb7214723ffc13901343df4b9c464ebf

7ef4312b0ae4d31db04a99673e8163768cc0a2a7062e731d

beb83b869f0509bf8009e90db5c8728e840e782d2cf928dae

35c2578ec55f0d11665a30b37f8636c08789976d8ee9fe9a5

c4a0435f0821738e51d6bdd6e6711a5acb620018658cce93

df37d7e85f9a0104a5845053995ce8

Public key token is 2dc940d5439468c2

创建好了公钥/私钥对,创建强命名程序集就很容易了。只需要把System.Reflection.AssemblyKeyFileAttribute特性加入源代码中就可以了:?[assembly:AssemblyKeyFile("MyCompany.keys")]

说明:公钥/私钥对文件的扩展名可以是任意的(也可以没有),因为编译的时候都是以元数据的格式读取的。

2.       程序集的部署方式

一个程序集有两种部署方式:

a)       私有方式

和应用程序部署在同一目录下的程序集称作私有部署程序集。弱命名程序集只能进行私有部署。

b)       全局方式

全局部署方式将程序集部署在一些CLR已确知的地方,当CLR搜索程序集时,它会知道这些地方去找。强命名程序集既可以进行私有部署,也可以进行全局部署。

3.       如何部署强命名程序集(Strong Name Assembly)和GAC

a)       GAC的概念

如果一个Assembly要被多个应用程序访问,那么他就必须放在一个CLR已确知的目录下,并且CLR在探测有对该Assembly的引用时,它必须能自动该目录下寻找这个程序集。这个已确知的目录称作GACGlobal Assembly Cache),就是全局程序集缓存。它一般位于下面的目录下:<System Drive>:/Windows/Assembly/GAC

GAC的作用就是提供给CLR一个已知的确定的目录去寻找引用的程序集。

b)       GAC的内部结构

GAC是一个特殊的结构化的目录,用Windows Explorer浏览你会以为它只是一个包含很多程序集的普通目录。其实不是这样的,在命令行下查看,你会发现它实际上包含很多子目录,子目录的名字和程序集的名称是相同的,但它们都不是实际的程序集,实际的程序集位于程序集名对应的目录下。比如进入GCFWK子目录,我们会发现其中又有很多的子目录。机器内每一个安装GACGCFWK.dllGCFWK中都会有一个子目录。

这里只有一个目录表明只有一个版本的GCFWK程序集被安装。实际的程序集保存在每一个对应的版本目录下。目录的名称以下划线的形式分割为“(Version)_(Culture)_(PublicKeyToken)”

GCFWK的语言文化信息为netture,就表示为0.0.0__bf5779af662fc055”表示得意义是: “GCFWK, Version=1.0.0.0, Culture=neutral,PublicKeyToken=bf5779af662fc055” 如果语言文化信息为”ja”,就表示”1.0.0.0_ja_bf5779af662fc055”

表示得意义是: “GCFWK, Version=1.0.0.0, Culture=ja, PublicKeyToken=bf5779af662fc055”

c)       部署强命名程序集GAC

GAC包含很多子目录,这些子目录是用一种算法来产生的,我们最好不要手动将程序集拷贝GAC中,相反,我们应使用工具来完成这样的工作。因为这些工具知道GAC的内部结构J

在开发和测试中,最常用的工具就是GACUtil.exeGAC注册程序集跟COM注册差不多,但相对更容易:

1.       把程序集添加GAC中: GACUtil /i sample.dll (参数/i是安装的意思)

2.       把程序集移出GAC GACUtil /u sample.dll (参数/u就移除的意思)

注意:不能将一个弱命名程序集安装GAC中。

如果你试图把弱命名程序集加入GAC中,会收错误信息:

Failure adding assembly to the cache: Attempt to install an assembly without a strong name”

d)       强命名程序集的私有部署

把程序集安装GAC有几个好处。首先,GAC使得很多程序可以共享程序集,这从整体上减少了使用的物理内存;其次,我们很容易将一个新版的程序集部署 GAC中,并通过一种发布者策略(差不多就是一种重定向方法,比如将原来引用版本为1.0.0.0程序集的程序,通过更改它的配置文件,转而让程序去引用版本为2.0.0.0的程序集)来使用新版本;最后,GAC还提供了对不同版本程序集的并存(side-by-side)管理方式。但是,GAC的安全策略通常只允许管理员更改,同时,向GAC中安装程序集也破坏了.NET框架的简单拷贝部署的许诺。

除了向GAC或者以私有部署方式部署强命名程序集之外,我们还可以将强命名程序集部署在仅为一小部分程序知道的某个任意目录下。配置每一个应用程序的 XML配置文件,让它们指向一个公有目录,这样,在运行时,CLR将知道哪里去找这个强命名程序集。但这样又有可能会引发”DLL Hell”的问题,因为没有哪个程序可以控制这个程序集何时被卸载。这在.NET中也是不被鼓励的。



强名称工具 (Sn.exe)

强名称工具有助于使用强名称对程序集进行签名。Sn.exe 提供用于密钥管理、签名生成和签名验证的选项。

sn [-quiet][option [parameter(s)]]
选项说明
-c [csp]将默认加密服务提供程序 (CSP) 设置为用于强名称签名。此设置应用于整台计算机。如果不指定 CSP 名称,则 Sn.exe 将清除当前设置。
-d container强名称 CSP 中删除指定的密钥容器。
-D assembly1 assembly2验证两个程序集是否只是签名不同。这经常用作使用不同的密钥对重新为程序集创建签名后的检查。
-e assembly outfileassembly 中提取公钥并将其存储在 outfile 中。
-h显示此工具的命令语法和选项。
-i infile container从指定密钥容器中的 infile 安装密钥对。密钥容器位于强名称 CSP 中。
-k outfile生成一个新的密钥对并将其写入指定的文件。
-m [y| n]指定密钥容器是计算机特定的还是用户特定的。如果指定 y,则密钥容器是计算机特定的。如果指定 n,则密钥容器是用户特定的。

如果既没有指定 y 也没有指定 n,则此选项显示当前设置。

-o infile [outfile]infile 中提取公钥并将其存储在 .csv 文件中。公钥的每一字节都由逗号分隔。这种格式对于通过硬编码在源代码中将公钥作为初始化数组引用很有用。如果不指定 outfile,则此选项将输出放到剪贴板上。
-p infile outfileinfile 中的密钥对提取公钥并将其存储在 outfile 中。此公钥可用于通过程序集链接器 (Al.exe)/delaysign+ /keyfile 选项对程序集进行延迟签名。如果延迟创建程序集的签名,则在编译时只设置公钥,并在文件中为以后知道公钥时添加的签名保留空间。
-pc container outfilecontainer 中的密钥对中提取公钥并将其存储在 outfile 中。
-q[uiet]指定安静模式;取消显示成功消息。
-R assembly infile使用 infile 中的密钥对,重新签名先前已签名的程序集或延迟已签名的程序集。
-Rc assembly container使用容器中的密钥对,重新签名先前已签名的程序集或延迟已签名的程序集。
-t[p] infile显示存储在 infile 中的公钥的标记。以前必须使用 -p 生成了 infile 的内容。

Sn.exe 使用公钥中的哈希函数计算该标记。为节省空间,公共语言运行库在记录对具有强名称的程序集的依赖性时,将公钥标记存储在清单中,作为对另一个程序集的引用的一部分。-tp 选项除显示标记外还显示公钥。

-T[p] assembly显示 assembly 的公钥标记。assembly 必须是包含程序集清单的文件名。

Sn.exe 使用公钥中的哈希函数计算该标记。为节省空间,公共语言运行库在记录对具有强名称的程序集的依赖性时,将公钥标记存储在清单中,作为对另一个程序集的引用的一部分。-Tp 选项除显示标记外还显示公钥。

-v assembly验证 assembly 中的强名称,其中 assembly 是包含程序集清单的文件名。
-vf assembly验证 assembly 中的强名称。与 -v 选项不同,-vf 强制验证,即使已使用 -Vr 选项禁用了验证。
-Vl列出此计算机上的强名称验证的当前设置。
-Vr assembly [userlist] [infile]注册 assembly 以跳过验证。或者,可以指定用逗号分隔的用户名列表。如果指定 infile,则验证保持启用,但 infile 中的公钥将用于验证操作。可以 *, strongname 的形式指定程序集,以注册所有具有指定强名称的程序集。strongname 应指定为十六进制数字的字符串以表示标记形式的公钥。参见 -t-T 选项以显示公钥标记。
警告   仅在开发期间使用此选项。将程序集添加到跳过验证列表会产生安全漏洞。如果将某程序集添加到跳过验证列表中,则恶意的程序集可以通过使用该程序集的完全限定程序集名称来隐藏身份,完全限定程序集名称由程序集名称、版本、区域性和公钥标记组成。这使恶意程序集也可以跳过验证。
-Vu assembly注销 assembly,不跳过验证。应用于 -Vr 的同一程序集命名规则也应用于 -Vu
-Vx移除所有验证跳过项。
-?显示此工具的命令语法和选项。
注意   所有 Sn.exe 选项都区分大小写,并且必须完全按上面显示的样子键入才可以被此工具识别。
备注

-R-Rc 选项对被延迟签名的程序集很有用。在此方案中,编译时只设置公钥,并且以后知道私钥时执行签名。

示例

下面的命令创建一个新的随机密钥对并将其存储在 keyPair.snk 中。

sn -k keyPair.snk

下面的命令将 keyPair.snk 中的密钥存储在强名称 CSP 中的容器 MyContainer 中。

sn -i keyPair.snk MyContainer

下面的命令从 keyPair.snk 中提取公钥并将其存储在 publicKey.snk 中。

sn -p keyPair.snk publicKey.snk

下面的命令验证程序集 MyAsm.dll

sn -v MyAsm.dll

下面的命令从默认 CSP 中删除 MyContainer

sn -d MyContainer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值