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

有一个类库项目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. C++ 实现memcpy和strcpy

    /** * @Method: Memcpy * @Access: public * @Return: void * * @Param : dst - 目的起始地址 * @Param : src - 源 ...

  2. Linux基础入门学习笔记之二

    第三节 用户及文件权限管理 Linux用户管理 Linux是可以实现多用户登录的操作系统 查看用户 who命令用于查看用户 shiyanlou是当前登录用户的用户名 pts/0中pts表示伪终端,后面 ...

  3. 20155309南皓芯2016-2017 2《Java程序设计》第一周学习总结

    关于java学习笔记的思考问题 第一章:JDK与JRE,JVM之间有没有必然的联系 第二章:可执行文件夹找到相关链接库 第三章:for与while循环的用法与比较,break与continue跳出的注 ...

  4. Asp.net Vnext 调试源码

    概述 本文已经同步到<Asp.net Vnext 系列教程 >中] 如果想对 vnext深入了解,就目前为止太该只有调试源码了 实现 github上下载源码 选择对应的版本,版本错了是不行 ...

  5. 【LOJ】 #2305. 「NOI2017」游戏

    题解 枚举x所在的地图的颜色,然后2-SAT建边 如果v所在的地图刚好是不能选的,那么u这边只能选另一种颜色 否则就是u的颜色到v的颜色 v的另一种颜色到u的另一种颜色 代码 #include < ...

  6. 8-7 Unique Snowflakes UVA11572

    输入一个长度为n n<=10 6  的序列A  找到一个尽量长的连续子序列  使得该序列中没有相同的元素 用滑动窗口法   时间复杂度n  好神奇 此题非常经典 map   410ms #inc ...

  7. ubuntu16.04 更换源

    1.备份 sudo cp /etc/apt/source.list /etc/apt/source.list.bak 2.打开/etc/apt/source.list,并删除所有内容 sudo ged ...

  8. Python之路【第十一篇】: 进程与线程

    阅读目录 一. cpython并发编程之多进程1.1 multiprocessing模块介绍1.2 Process类的介绍1.3 Process类的使用1.4 进程间通信(IPC)方式一:队列1.5 ...

  9. MXNet 中的几个数据集

    from mxnet import gluon def transform(data, label): return data.astype('float32') / 255., label.asty ...

  10. Stringbuilder & Stringbuffer

    StringBuilder和StringBuffer的父类都是继承了 AbstractStringBuilder, 他们各自的append方法都是调用了 super.append(str), 但是一个 ...