SN是.NET中提供生成密钥的工具,在对集进行强签名的时候,需要用到一个公钥对,用这个工具,可以生成一个密钥文件,同时可以查看文件里面的公钥,并且可以使用这个工具来对程序集进行强签名。延迟签名的时候也需要使用这个工具。这个工具一般是在C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin 或 C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin 这个路径下。启动这个工具,界面如下:
为程序集赋予强名称的步骤如下
第一步:生成公钥/私钥对(生成martinsnk.snk文件) sn -k D:\martinsnk.snk说明:1)每次调用sn时,创建的私钥/密钥对文件都不相同,不仅自己的计算机每次生成的不同,其他所有计算机生成的也不相同。 2)关于这个文件的命名,如果是个人,可以以自己的名字命名;如果是团队、组织或公司,可以以组织名称来命名。如TraceFact公司可以为TraceFact.snk。 3)在大多数情况下,个人和组织只需要一个snk文件就可以了,以后创建强名称的时候,都使用这个snk文件。由于会使用snk文件的私钥对强名称程序集进行签名,因此这个文件必须严密保护。第二步:生成公钥(martinsnk.pk),使用sn.exe执行命令: sn -p D:\martinsnk.snk D:\martinsnk.pk第三部:可以使用sn来查看公钥具体值和公钥标记,执行命令:
sn -tp D:\martinsnk.pk说明:公钥的字节数很长,有128字节,操作起来很不方便。因此,对公钥进行了散列运算,获得了一个它的8字节的哈希值,也就是公钥标记。对于开发者来说,只要关注公钥标记就可以了。
现在,就可以唯一确定一个程序集了。此时,如果还是按照为程序集加特性标记的方式来写入PublicKeyToken,例如:[assembly:PublicKeyToken("c3084eebf658444c")]
那么和使用GUID是完全一样的,没有起到防仿冒的作用。由于公钥和公钥标记是公开的,其他人也可以为自己的程序集设置和你相同的PublicKeyToken。显然,使用程序集特性标记来写入PublicKeyToken并不合适。正确的使用方式,这里举一个例子,比如,当使用Visual Studio时,可以先进入项目属性,然后进入“签名”选项卡,勾选“为程序集签名”选项,点击“选择强名称密钥文件”下拉框,然后选中martinsnk.snk密钥文件,如图:
仅就程序集唯一性来说,使用公钥标记就够了。但使用公钥/私钥对的作用不仅限于此,还可以防篡改和数字签名(这里就不多说了)。
说道强名称程序集,这里就谈谈全局程序集缓存(GAC,Global Assembly Cache)。
为什么会用到程序集缓存呢?比如这种情况,当多个程序引用同一个程序集时,可以将它放在一个公共的共享文件夹,但是这个文件夹又不能像System32文件夹一样,以文件名来对程序集进行区分,否则就会出现后安装的程序集将之前安装的同名程序集覆盖的问题。在.NET中,这个特殊的文件夹叫做全局程序集缓存(GAC,Global Assembly Cache),它的位置是C:\Windows\Assembly,进入这个文件夹是这样:
GAC文件夹的底层结构与普通文件夹是不同的,因此不能直接将程序集复制进去,而要通过GAC实用工具来完成。这个实用工具的名称叫做GacUtil.exe,打开“Visual Studio命令提示”,可以通过下面的语句将程序集安装至GAC:
gacutil -i D:\martinsnk.dll
在从GAC中卸载程序集时,可以使用下面的命令:
gacutil -u martinsnk(还可以更加具体准确点:gacutil -u martinsnk,Version=1.1.0.0,Culture=neutral,PublicKeyToken=81e738080cc1bdc3)
卸载程序集时需要注意:
1)在卸载程序集时,不要输入.dll后缀,martinsnk.dll是文件名,不是程序集名。如果输入.dll后缀,会出现错误提示“找不到与以下内容匹配的程序集:martinsnk.dll”。
2)程序集的全名称之间默认是存在空格的,需要删掉空格,否则会出现“未知选项Version=1.1.0.0”,因为命令行的选项是以空格为分隔的,会将Version误认为一个选项。3)如果输入命令gacutil-u martinsnk,取消后面的版本号、区域性、公钥标记,则会卸载所有名称为martinsnk的程序集。将程序集安装到GAC之后,就可以对它进行引用了。对于开发者来说,引用GAC程序集其实与引用非强名称程序集并无明显区别,但在引用GAC程序集时,并不会像引用非强名称程序集一样,复制一份程序集的副本到应用程序根目录下。现在,我们也仿照微软的做法来引用GAC程序集:
1)创建一个文件夹D:\SharedAssembly,这个文件夹下存放了所有我们自己的、安装到GAC中的程序集。将文件夹结构创建为:D:\SharedAssembly\程序集名\版本号。
对于本例来说,是 D:\SharedAssembly\martin\v1.1。然后将martin.dll复制进来。
2)运行gacutil-i D:\SharedAssembly\martin\v1.1\martin.dll,将程序集安装至GAC。3)打开之前创建的控制台项目ConsoleApp,然后引用D:\SharedAssembly\martin\v1.1下的martin。4)生成ConsoleApp,查看一下Debug\bin文件夹,会发现并没有martin.dll文件。这是引用GAC程序集与引用非强名称程序集的一个重要区别:不会复制一份martin.dll的副本到应用程序根目录。5)查看ConsoleApp项目中“引用”文件夹下martin的属性,路径为:D:\Shared-Assembly\martin\v1.1\martin.dll。6)运行ConsoleApp,一切正常。作为试验,删掉D:\SharedAssembly\martin\v1.1\文件夹下的martin.dll,再次运行ConsoleApp,依然没有问题,说明运行时加载的是GAC中的程序集。
QQ交流群:338665589(本群创建于2017-5-10)