为什么需要强名称程序集和数字签名

有一个类库项目ClassLib,对应的程序集是ClassLib.dll。当前控制台项目引用ClassLib.dll程序集的方式有2种:
1、通过添加现有项目
文件→添加→现有项目→选择"ClassLib.csproj",把项目引入到当前控制台所在解决方案→右键控制台项目"引用"→添加引用→解决方案→项目→选择ClassLib项目

 

2、通过把程序集复制到当前项目文件夹下
在控制台项目下创建Library文件夹→把程序集ClassLib.dll拷贝到Library文件夹下→在控制台项目下引用该程序集

 

程序集的属性:
● 复制本地:True,表示在编译时会自动复制一份ClassLib.dll到当前项目bin/Debug中。
● 路径,表示ClassLib.dll程序集的所在位置。

 

2种引用程序集方式比较:
● 通过项目引用,由于路径总是指向ClassLib项目下bin/Debug,总能获得最新的ClassLib.dll程序集
● 通过程序集引用,获得的ClassLib.dll程序集可能不是最新版本

 

为什么需要强名称程序集?
● 如果不的项目想引用ClassLib.dll程序集的不同版本,如何区分?
● 如果其它公司的的程序集也叫ClassLib.dll,并且被引入,如何区分?
● 程序集的公司名、版本号、GUID等显式地声明在Properties/AssemblyInfo.cs中,如何防止篡改?

可以通过为程序集赋予强名称和为程序集加数字签名来解决上面的问题。

 

  为程序集赋强名称

→在F盘m文件夹下创建公匙/私匙(Public Key/Private Key)

在"开发人员命令提示"中输入:

于是,在F:\m文件夹下多了Darren.snk

注意:
● 公匙/私匙采用非对称RSA加密方式,并结合使用了散列函数(SHA1)
● 每次调用sn时,创建的公匙/私匙都不同

→从密匙文件中提取公匙部分,将公匙另存为一个公匙文件

 
于是,在F:\m文件夹下多了Darren.pk公匙文件

→查看Darren.pk公匙文件

→在VS中,右键项目--属性--签名--勾选"为程序集签名"--选择刚创建的密匙文件,为程序集ClassLib.dll创建密匙

另外,还可以通过C#编译器为某个主程序创建密匙:
csc /keyfile:Darren.snk Program.cs

→查看ClassLib.dll的PublicKeyToken

注意:
sn -T 程序集,这里的T一定要大写,否则报"未能将密匙转换为标记 无效的程序集公匙"错。

 

  全局程序集缓存

当多个程序引用同一个程序集时,可以将程序集放到一个共享文件夹,这个文件夹不是以文件名来对程序集进行区分,这个特殊的文件夹叫做"全局程序集缓存(Global Assembly Cache,GAC)",它的位置在C:\Windows\aasembly,在运行阶段会使用这里的程序集。而在开发和编译阶段使用的是:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\版本号\下的程序集。

查看System.Data的属性:

 

□ 自定义开发、编译阶段的程序集缓存及文件夹

→创建F:\SharedAssembly\ClassLib\v1.1
→把ClassLib.dll拷贝到F:\SharedAssembly\ClassLib\v1.1文件夹中
→将程序集ClassLib.dll安装到GAC中

→控制台项目引用F:\SharedAssembly\ClassLib\v1.1\ClassLib.dll
→生成项目,发现在控制台项目的bin\Debug下没有ClassLib.dll

可见,ClassLib.dll在全局程序集缓存中,不会在应用程序下拷贝一份ClassLib.dll。

→运行如下程序

        static void Main(string[] args) 
        { 
            Class1 c = new Class1(); 
            Console.WriteLine(c.GetContent()); 
            Console.ReadKey(); 
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

结果:

→查看ClassLib.dll的属性

说明在开发和编译阶段使用的是F:\SharedAssembly\ClassLib\v1.1\ClassLib.dll。

→删除F:\SharedAssembly\ClassLib\v1.1下的ClassLib.dll,运行程序,还是得到与删除之前相同的结果

说明已经在使用全局程序集缓存了。

→再使用反射查看全局程序集缓存中的ClassLib.dll中的全名

        static void Main(string[] args) 
        { 
            Assembly asm = Assembly.GetAssembly(typeof(ClassLib.Class1)); 
            Console.WriteLine(asm.FullName); 
            Console.ReadKey(); 
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

结果:

→从GAC中卸载程序集

gacutil -u ClassLib,Version=1.1.0.0,Culture=neutral,PublicKeyToken=....

 

  延迟签名

为什么需要延迟签名?

在团队开发中,如果将密匙文件提供给每位开发者,将会增加泄漏密匙文件的可能;如果不提供给团队成员,意味着在开发、编译、测试阶段只能使用非强名称程序集,在项目打包部署之前可能存在这样的做法:

 

1、删除掉之前所有引用的非强名称程序集,重新引用一遍签名过的强名称程序集,然后再全部重新编译一遍。

2、使用签名过的强名称程序集去覆盖同名的非强名称程序集,如果运行程序,会抛出异常"未能加载文件或程序集,找到的程序集清单定义与程序集引用不匹配"。

 

延迟签名可以很好地解决上面的问题:

→使用公匙进行标记,但是没有用私匙进行签名。

→缺少私匙签名的强名称程序集相当于一个被篡改过的强名称程序集,在正常情况下,CLR会拒绝加载它,并抛出"未能加载文件或程序集,强名称验证失败",为此,开发者需要指示CLR忽略对延迟签名程序集的验证,运行延迟签名程序集运行。

→在程序最终部署前,持有私匙的管理人员使用密匙文件对延迟签名程序集重新签名,也并不需要重新再编译引用了延迟签名程序集。

□ 延迟签名实例

→先把Darren.snk和以上ClassLib.dll程序集中的Class1.cs文件放到F:\asm中

→从公匙/私匙文件中提取公匙,单独保存在公匙文件中

在F:\asm文件中就多了Darren.pk公匙文件

→使用C#编译器csc.exe对程序集进行编译,并使用/delaysign+指定Darren.pk公匙文件

在F:\asm文件中就多了ClassLib.dll延迟签名程序集文件

→指示CLR或略对程序集的验证,该指令只对本台电脑有效。

如果想让CLR忽略对多个程序集的验证,可以通过通配符,并指定公匙文件:

D:\asm\sn -Vr * Darren.pk

→在程序发布之前,使用完整的公匙/私匙文件对程序集进行签名

 

  总结

● 通过使用公匙/私匙文件为程序集签名,创建强名称程序集,可以并防止程序集被篡改和仿冒。

● 在团队开发中,可以考虑使用延迟签名,减少公匙/私匙文件泄漏的风险,并防止程序集被篡改和仿冒。

 

参考资料:

《.NET之美》--张子阳,感谢写了这么好的书!

29防止程序集被篡改仿冒,全局程序集缓存GAC的更多相关文章

  1. 如何将程序集安装到全局程序集缓存GAC

    针对一些类库项目或用户控件项目(一般来说,这类项目最后编译生成的是一个或多个dll文件),在程序开发完成后,有时需要将开发的程序集(dll文件)安装部署到GAC(全局程序集缓存)中,以便其他的程序也可 ...

  2. C#程序集系列11,全局程序集缓存

    全局程序集缓存(GAC:Global Assembly Cache)用来存放可能被多次使用的强名称程序集.当主程序需要加载程序集的时候,优先选择到全局程序集缓存中去找寻需要的程序集. 为什么需要全局程 ...

  3. 无法安装或运行此应用程序。该应用程序要求首先在"全局程序集缓存(GAC)"中安装程序集

    在做winform程序发布时遇到了这个问题,在我的机子上是可以正常运行的,但到别人的机子上就出现了这个错误.为此问题头疼了一上午终于搞定! 遇到这个问题一定是配置环境的原因, 1.你可以在程序  发布 ...

  4. 全局程序集缓存GAC

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

  5. 【C# 程序集】在.net中使用GAC 全局程序集缓存

    原文地址:https://blog.alswl.com/2011/01/gac/ GAC GAC是什么?是用来干嘛的?GAC的全称叫做全局程序集缓存,通俗的理解就是存放各种.net平台下面需要使用的d ...

  6. Gacutil.exe(全局程序集缓存工具)

    全局程序集缓存 .NET Framework (current version) 其他版本 安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存.全局程序集缓存中存储了专门 ...

  7. 【转】Gacutil.exe(全局程序集缓存工具)

    全局程序集缓存工具使您可以查看和操作全局程序集缓存和下载缓存的内容. 安装 Visual Studio 和 Windows SDK 时会自动安装此工具. 要运行工具,我们建议您使用 Visual St ...

  8. C#中的全局程序集缓存定义

    安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存.全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集. 应当仅在需要时才将程序集安装到全局程序集缓存中以 ...

  9. 全局程序集缓存工具(Gacutil.exe)用法详解

    全局程序集缓存工具 (Gacutil.exe) 全局程序集缓存工具使您可以查看和操作全局程序集缓存和下载缓存的内容. 复制 gacutil [options] [assemblyName | asse ...

随机推荐

  1. AdvStringGrid 列宽度、列移动、行高度、自动调节

    那么有没有办法,让客户自己去调整列的宽度呢? 那么有没有办法 让列宽度.行高度 随着内容而自动变换呢: unit Unit5; interface uses Winapi.Windows, Winap ...

  2. 如何使用 JMeter 调用你的 Restful Web Service?进行简单的压力测试和自动化测试

    表述性状态传输(REST)作为对基于 SOAP 和 Web 服务描述语言(WSDL)的 Web 服务的简单替代,在 Web 开发上得到了广泛的接受.能够充分证明这点的是主流 Web 2.0 服务提供商 ...

  3. SQL group 分组查询

    1.使用group by进行分组查询  在使用group by关键字时,在select列表中可以指定的项目是有限制的,select语句中仅许以下几项:  被分组的列 为每个分组返回一个值得表达式,例如 ...

  4. Web前端开发最佳实践(8):还没有给CSS样式排序?其实你可以更专业一些

    前言 CSS样式排序是指按照一定的规则排列CSS样式属性的定义,排序并不会影响CSS样式的功能和性能,只是让代码看起来更加整洁.CSS代码的逻辑性并不强,一般的开发者写CSS样式也很随意,所以如果不借 ...

  5. bzoj 3926 转换+广义后缀自动机

    思路:重点在于叶子节点只有20个,我们把叶子节点提到根,把20个trie图插入后缀自动机,然后就是算有多少个本质不同的字串. #include<bits/stdc++.h> #define ...

  6. codeforces Educational Codeforces Round 9 E - Thief in a Shop

    E - Thief in a Shop 题目大意:给你n ( n <= 1000)个物品每个物品的价值为ai (ai <= 1000),你只能恰好取k个物品,问你能组成哪些价值. 思路:我 ...

  7. php匹配邮箱正则

    php匹配邮箱正则 '/[a-z0-9&\-_.]+@[\w\-_]+([\w\-.]+)?\.[\w\-]+/is'

  8. Python实现代码行数统计工具

    我们经常想要统计项目的代码行数,但是如果想统计功能比较完善可能就不是那么简单了, 今天我们来看一下如何用python来实现一个代码行统计工具. 思路:首先获取所有文件,然后统计每个文件中代码的行数,最 ...

  9. java的组合和继承

    其实我第一次学习java 的时候根本没有听说过组合这个名词,老师也更没有讲解过,我一直以为是我自己落掉了什么知识点,其实不是的,组合这个名词暂且把它定义为一个思维性的东西吧,相信读者都接触过了,但是并 ...

  10. JAVAEE——宜立方商城08:Zookeeper+SolrCloud集群搭建、搜索功能切换到集群版、Activemq消息队列搭建与使用

    1. 学习计划 1.solr集群搭建 2.使用solrj管理solr集群 3.把搜索功能切换到集群版 4.添加商品同步索引库. a) Activemq b) 发送消息 c) 接收消息 2. 什么是So ...