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

有一个类库项目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. 用django-cors-headers做跨域

    什么是CORS? CORS(跨域资源共享,Cross-Origin Resource Sharing)是一种跨域访问的机制,可以让Ajax实现跨域访问. 其实,在服务器的response header ...

  2. 如何验证一个地址可否使用—— MmIsAddressValid函数分析

    又是一篇内核函数分析的博文,我个人觉得Windows的内核是最好的老师,当你想实现一个功能之前可以看看Windows内核是怎么做的,说不定就有灵感呢:) 首先看下官方的注释说明: /*++ Routi ...

  3. .size和.empty

    坑爹的list容器size方法--为了splice居然把复杂度设计为O(N)? 能用empty的时候,不要用size empty可以保证常量复杂度,但list的size不保证 链表长度必须要遍历全部的 ...

  4. jenkins中管理用户

    jenkins中管理用户: 管理用户权限

  5. Ubuntu18.04 n卡配置

    一.背景 最近安装了ubutnu18.04后,安装系统后重启卡住,强制关机重启后,又在开机界面紫屏卡住,上网搜索后发现是n卡的驱动问题,使用以下方法安装驱动后成功解决该问题. 二.解决方法 1.进入恢 ...

  6. FPGA In/Out Delay Timing Constaint

    先简单说说这段时间遇到的问题.FPGA采集前端scaler的视频数据.像素时钟(随路时钟),视频数据,行场同步,DE.这些信号进入FPGA后.通过CSC(颜色空间转换).输出后的图像有噪点.通过查看时 ...

  7. Django项目从零开始的大概脉络

    Django项目从零开始脉络 创建虚拟环境,隔离项目python环境:mkvirtualenv -p /usr/bin/python3.6 envname 安装Django:pip install d ...

  8. [leetcode DP]53. Maximum Subarray

    Find the contiguous subarray within an array (containing at least one number) which has the largest ...

  9. iOS Sprite Kit教程之真机测试以及场景的添加与展示

    iOS Sprite Kit教程之真机测试以及场景的添加与展示 IOS实现真机测试 在进行真机测试之前,首先需要确保设备已经连在了Mac(或者Mac虚拟机)上,在第1.9.1小节开始,设备就一直连接在 ...

  10. 学习Git操作的好资源

    网上资源很多,极大的方便了我们学习新东西. 今天找到了几个简单明了的Git教程,用以备录共享. Learn Git Branching  http://pcottle.github.io/learnG ...