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

有一个类库项目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. ZOJ 3962 Seven Segment Display(数位DP)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...

  2. (五)动态SQL

    第一节:if条件 第二节:choose,when和otherwise条件 第三节:where条件 1.自动加上where: 2.如果where子句以and或者or开头,则自动删除第一个and或者or: ...

  3. 基于docker 搭建Prometheus+Grafana

    一.介绍Prometheus Prometheus(普罗米修斯)是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的.随着发展,越来越多公司和组织接受采 ...

  4. Python 安装 pytesser 处理验证码出现的问题

    今天这个问题困扰了我好久,开始直接用 pip install pytesseract 安装了 pytesseract 然后出现了如下错误 Traceback (most recent call las ...

  5. 20155309 2016-2017-2《Java程序设计》课程总结

    预备作业1http://www.cnblogs.com/nhx19970709/p/6155580.html 第一次写博客,也是第一次用Markdown,具体流程都还不是很熟悉 预备作业2http:/ ...

  6. .NET异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  7. html5+css3 h5页面生成的思路

    <!DOCTYPE html><html style="height: 100%;"> <head> <meta charset=&quo ...

  8. beeshell —— 开源的 React Native 组件库

    介绍 beeshell 是一个 React Native 应用的基础组件库,基于 0.53.3 版本,提供一整套开箱即用的高质量组件,包含 JavaScript(以下简称 JS)组件和复合组件(包含 ...

  9. python 与 mongodb的交互--更新操作

    这里只要讨论python与mongod交互的时候insert的问题: from pymongo import * def insert_func(): try: client_obj= MongoCl ...

  10. Linux内核代码

    全局描述符表GDT(Global Descriptor Table): (1)在整个系统中,全局描述符表(注意这里是表,表只有一张)GDT只有一张(一个处理器对应一个GDT). (2)GDT可以被放在 ...