.net程序集强名称签名实践

引用:  http://www.cnblogs.com/cpcpc/archive/2011/01/17/2123086.html
强名称是由程序集的标识加上公钥和数字签名组成的。其中,程序集的标识包括简单文本名称、版本号和区域性信息(如果提供的话)。强名称是使用相应的私钥,通过程序集文件(包含程序集清单的文件,并因而也包含构成该程序集的所有文件的名称和散列)生成的。Microsoft? Visual Studio? .NET 和在 .NET Framework SDK 中提供的其他开发工具能够将强名称分配给一个程序集。强名称相同的程序集应该是相同的。
通过签发具有强名称的程序集,您可以确保名称的全局唯一性。强名称还特别满足以下要求:
1) 强名称依赖于唯一的密钥对来确保名称的唯一性。任何人都不会生成与您生成的相同的程序集名称,因为用一个私钥生成的程序集的名称与用其他私钥生成的程序集的名称不相同。
2) 强名称保护程序集的版本沿袭。强名称可以确保没有人能够生成您的程序集的后续版本。用户可以确信,他们所加载的程序集的版本出自创建该版本(应用程序是用该版本生成的)的同一个发行者。
强名称提供可靠的完整性检查。通过 .NET Framework 安全检查后,即可确信程序集的内容在生成后未被更改过。但请注意,强名称中或强名称本身并不暗含信任级别,例如由数字签名和支持证书提供的信任。
虽然强名称是.NET加密领域的元老,也是微软推荐的应用程序保护机制,但是由于托管程序可以被反汇编成IL代码,更改或者去除强名称也就成了可能。强名称的保护强度也因此大大减弱。
9.2.1 使用强名称保护代码完整性

当我们从互联网上下载一个程序集供本地调用的时候,如何保证这个程序集是未经第三方恶意篡改过的呢?如果两个程序集的名称、大小、版本号都相同是不是就意味着这两个程序集文件就相同了呢?

在.NET平台下区分程序集采用的方法和Win32一样,使用名称,但是名称有强弱之分。弱名称包含版本号、程序集名称和文化。强名称在弱名称的基础上添加了数字签名。强名称的作用主要有三个:一是区分不同的程序集;二是确保代码没有被篡改过;三是在.NET中,只有强名称签名的程序集才能放到全局程序集缓存中。

使用强名称来包含程序集我们首先要生成用于非对称加密的密钥对,这对密钥将用于程序集的签署和验证。签署和验证的流程如图9-3所示。

图9-3 签署(上)与验证(下)强名称流程

如图9-3所示,在进行强名称签名的时候我们首先对程序集(不包括DOS头和PE头)进行Hash运算,得到文件的散列值,然后使用私钥对散列值进行加密,得到密文。然后将公钥、公钥标识(对公钥进行SHA-1散列运算后得到的密文的最后8个字节)和密文三个信息保存在程序集中。在加载该程序集时,首先对该程序集进行Hash运算得到一个Hash值(我们称之为“新Hash值”),然后从程序集中提取公钥对密文解密得到原始的Hash值,如果两个Hash值相同,即通过验证。

对程序集签名有正常签名和延迟签名两种方式。

延迟签名是在我们开发人员只具有对公钥的访问权限而没有对私钥的访问权限时使用的。这是我们可以先将程序集编译并预留签名空间。此时的程序集无法正常运行和调试。

9.2.2.1 在SDK中创建强名称签名的程序集

对程序集进行强名称签名,我们要首先准备好密钥。在本书的第6章中,介绍过使用Visual Studio SDK中提供的强名称工具(Sn.exe)可以生成密钥对。我们使用如图9-4的命令生成一个新的密钥对并保存到本地文件test.snk中。

图9-4 生成密钥文件

接下来我们新建一个控制台测试项目StrongName,主要代码如代码清单9-3所示。

代码清单9-3 StrongName项目主要代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace StrongName
{
class Program
    {
static void Main(string[] args)
        {
            string aa = "";

Assembly ass = Assembly.GetExecutingAssembly();
Console.WriteLine(ass.FullName.ToString());
byte[] pKey = ass.GetName().GetPublicKey();
byte[] pKeyToken = ass.GetName().GetPublicKeyToken();
string pKeyString = GetString(pKey);
string pKeyTokenString = GetString(pKeyToken);
Console.WriteLine("公钥是:{0}",pKeyString);
Console.WriteLine("公钥标识是{0}", pKeyTokenString);
Console.Read();
        }
private static string GetString(byte[] bytes)
        {
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
            {
                sb.Append(string.Format("{0:x}",b));
            }
return sb.ToString();
        }
    }
}

代码清单9-3的代码很简单,使用反射来列举当前程序集的名称和使用的公钥及公钥标识。在没有对程序集进行强名称签名时我们运行程序得到如图9-5的结果。

图9-5 没有对程序集进行强名称签名时代码清单9-3的运行结果

下面我们在命令行对C#编译器(csc.exe)执行如图9-4所示的命令。

图9-4 对程序集应用强名称

现在我们再来执行刚才生成的Program.exe,看看结果如何,如图9-5所示。

图 9-5 对程序集进行强名称签名之后代码清单9-3的运行结果

从图9-5我们可已看出,执行强名称签名之后我们成功的输出公钥和公钥标识。

为了使编译器能自动为代码进行强名称签名我们可以为代码添加特性指明强名称签名需要的密钥文件。添加特性之后的代码示例如代码清单9-4所示。

代码清单9-4 使用特性进行强名称签名

using System;

using System.Reflection;

[assembly:AssemblyKeyFileAttribute("TestKey.snk")]

代码清单9-4使用 AssemblyKeyFileAttribute 指定包含密钥对的文件的名称。

当我们需要对程序集进行延迟签名的时候,我们需要对 AssemblyDelaySignAttribute特性和AssemblyKeyFileAttribute 联合使用,形式如代码清单9-5所示

using System;

using System.Reflection;

[assembly:AssemblyKeyFileAttribute("myKey.snk")]

      [assembly:AssemblyDelaySignAttribute(true)]

如代码清单9-5,当我们需要对程序集延迟签名的时候,我们要指定包含公钥的文件并设定AssemblyDelaySignAttribute特性值为true。

9.2.2.2 在VS中创建强名称签名的程序集

在SDK中进行强名称签名未免麻烦了一些,下面我们以VS2010为例,讲解如何在Visual Studio中进行强名称签名的操作。

我们打开项目的属性,切换到签名页,如图9-6所示。

图9-6 项目的签名页

从图9-6中我可以看出,项目签名属性页中包含了三个大的配置项,第一个是为ClickOnce清单签名,第二个是为程序集签名,第三个是延迟签名。

为了使用ClickOnce部署发布应用程序,应用程序和部署清单必须使用公钥/私钥对进行强命名并使用Authenticod 技术进行签名。可以使用Windows证书存储区的证书或密钥文件为清单签名。也可以创建新的测试证书。

为程序集签名的选项中,我们可以选择密钥文件或者生成新的密钥文件来对程序集进行签名。

如果勾选了仅延迟签名,那么将对程序集进行延迟签名。

如图9-7,在创建了ClickOnce签名和程序集签名之后,项目自动添加了两个密钥文件StrongName_TemporaryKey.pfx和test.pfx。

图9-7 创建ClickOnce签名和程序集签名

强名称签名的程序集如果被篡改,那么CLR在加载该程序集进行完整性验证的时候就会失败。我们现在使用文本编辑工具打开StrongName.exe,在保证不破坏PE文件格式的前提下对其进行简单的修改,这里我只把程序中定义的变量aa替换成bb,如图9-8所示。

图9-8 修改强名称签名的程序集

修改之后,我们再次运行StrongName.exe,看到程序报出的异常为强名称验证失败,入图9-9所示。

图9-9 强名称验证失败

注意

1. 强名称签名的程序集不能引用未被签名的程序集。

2. 从 .NET Framework 3.5 版Service Pack 1开始,当程序集载入完全信任应用程序域(例如 MyComputer 区域的默认应用程序域)中时,将不会验证强名称签名。 这称为强名称跳过功能。 在完全信任环境中,对于已签名的完全信任程序集,无论这些程序集的签名是什么,对 StrongNameIdentityPermission 的要求将总是成功。 强名称跳过功能避免了在此情况下验证完全信任程序集的强名称签名所带来的不必要开销,从而可使程序集更快加载。

该跳过功能适用于用强名称签名并具有以下特征的任何程序集:

1) 完全受信任而没有 StrongName 证据(例如,具有 MyComputer 区域证据)。

2) 加载到完全受信任的 AppDomain 中。

3) 从该 AppDomain 的 ApplicationBase 属性下的位置加载。

4) 未经延迟签名。

可以对单独的应用程序或整个计算机禁用此功能。

1) 对所有应用程序禁用强名称跳过功能

在 32 位计算机上,在系统注册表中的 HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/.NETFramework 项下创建一个子项。 使用 DWORD 值为 0 的项名称 AllowStrongNameBypass。

在 64 位计算机上,在系统注册表中的 HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/.NETFramework 项下创建一个子项。 使用 DWORD 值为 0 的项名称 AllowStrongNameBypass。 在HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/Microsoft/.NETFramework 项下创建相同的子项。

2) 对单个应用程序禁用强名称跳过功能

打开或创建应用程序配置文件。添加下面的项:

<configuration>

<runtime>

< bypassTrustedAppStrongNames enabled="false" />

</runtime>

</configuration>

可通过移除该配置文件设置或将该特性设置为“true”为该应用程序恢复跳过功能。

9.2.2 引用强名称签名的程序集

引用强名称程序集的过程对我们来说都是透明,我们无需做额外的工作。但是我们可以通过这种方式来检验强名称程序集的作用。

如图9-10我们首先创建一个类库项目StrongNameReferenceLib,对其进行强名称签名。

图9-10 引用强名称程序集

接下来我们修改之前创建的StrongName项目,让它来引用StrongNameReferenceLib项目,调用其GetHello方法。

StrongNameReferenceLib项目的主要代码如代码清单9-6所示。

代码清单9-6 StrongNameReferenceLib项目主要代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StrongNameReferenceLib
{
public class Class1
    {
public static string GetHello()
        {
return "Hello";
        }
    }
}

修改后的StrongName项目代码如代码清单9-7所示。

代码清单9-7 StrongName项目代码

using System.Text;
using System.Reflection;
using StrongNameReferenceLib;
namespace StrongName
{
class Program
    {
static void Main(string[] args)
        {
Console.WriteLine(  Class1.GetHello());
Console.Read();
        }
    }
}

重新编译StrongName项目,我们现在得到新的StrongName.exe文件。使用ILDasm打开StrongName.exe文件,查看它的程序集清单,如图9-11所示。

图9-11 StrongName.exe程序集清单

在图9-11中的这份程序集清单,我们可以看到它引用了两个具有强名称签名的程序集,mscorlib和我们新创建的StrongNameReferenceLib,对两个程序集分别添加了版本和publickeytoken标识。

下面我们去除StrongNameReferenceLib的强名称签名,重新编译该项目,但我们并不重新编译StrongName项目,而用新生成的StrongNameReferenceLib.dll替换StrongName.exe之前引用的StrongNameReferenceLib.dll,看看会发生事情。结果如图9-12所示。

图9-12 StrongName项目替换dll之后结果

我们从图9-10的异常信息可以看到,StrongName项目找不到匹配的程序集。原因在于在StrongName的程序集清单中存储着PublicKeyToken值,而没有强名称签名的项目是没有该属性的。

9.2.3 强名称的脆弱性

上面的几个小节我们共同体验了强名称对程序集的保护方式和原理,但是这种保护的强度到底有多大呢?对恶意篡改者来说能得到有效的防御吗?我们先看下面的例子。

现在我们回到代码清单9-7,重新对StrongNameReferenceLib项目进行强名称签名,然后编译StrongName项目。现在在StrongName项目的bin目录里有StrongNam.exe和StrongNameReferenceLib.dll两个文件。然后使用ILDasm打开StrongNameReferenceLib.dll文件,转储为il文件,这里我使用记事本打开il文件,如图9-13所示。

图9-13 StrongNameReferenceLib.dll的IL源码

我们在.il文件中找到三处代码:publickkeytoken、publickey和hash,把对应的内容都删除,再重新使用ILAsm编译,这时该程序集的强名称就被成功的去除。

替换程序集的强名称方法基本相同。目前网络上有很多去除和替换强名称的工具,这里就不掩饰了。

那么我们应该通过什么样的方式来保护强名称不被去除或者篡改呢?这个问题我会在专门的文章里讨论。

代码清单9-5 对程序集延迟签名的特性声明

时间: 2024-10-22 16:57:35

.net程序集强名称签名实践的相关文章

为程序集添加强名称签名与数字证书签名

1.强名称签名(StrongName Sign) 强名称是由程序集的标识加上公钥和数字签名组成的.其中,程序集的标识包括简单文本名称.版本号和区域性信息(如果提供的话). 强名称是使用相应的私钥,通过程序集文件(包含程序集清单的文件,并因而也包含构成该程序集的所有文件的名称和散列)生成的. Microsoft® Visual Studio® .NET 和 Windows 软件开发包 (SDK) 中提供的其他开发工具能够向一个程序集分配多个强名称. 强名称相同的程序集应该是相同的. 通过签发具有强

程序集延迟签名-学习笔记

背景:为了防止私钥泄露,公司里只有少数几个人才能访问到私钥.其他同事开发时,也需要编译,但不又能给他们密钥,我们就需要使用延迟签名技术了. 1.       创建密钥对 sn -k mykeyfile.pfx 无密码保护,该文件需要安全保管 2.       安装密钥对到于强名称 CSP 中容器 sn -i mykeyfile.pfx mykeycontainer 3.       提取公钥文件 sn -p mykeyfile.pfx mykeyfile.pub 4.       显示公钥标记

程序集强签名和安装程序数字签名

一.程序集强签名 这个是.net 中的概念,为了保证程序集本身不被篡改,即保证这个程序集是你开发的,同时强命名使不同的程序可以指定不同的程序集版本,不影响其他应用程序.要对程序集进行强签名,无需使用购买的代码签名数字证书,用.NET提供的sn工具就可以生成snk文件,你只要保存好这个文件,就可以保证你的程序集的签名. 方法1:在SDK中创建强名称签名的程序集(Sn.exe),可以生成密钥对.我们使用如图9-4的命令生成一个新的密钥对并保存到本地文件test.snk中. 签名时使用编译器执行 CS

强命名程序集,签名,延迟签名

强命名程序集 如果一个程序集有一个唯一的标记,那么这个程序集就可以叫做强命名程序集.在.NET框架中是通过公钥/私钥加密来产生这个唯一标记的.一个强命名程序集包含四个唯一标志程序集的特性:文件名(没有扩展名),版本号,语言文化信息(如果有的话),公有秘钥. 这些信息存储在程序集的清单(manifest)中.清单包含了程序集的元数据,并嵌入在程序集的某个文件中.下面的字符串标识了二个不同的程序集文件: “MyType, Version=1.0.1.0,Culture=neutral, Public

未能加载文件或程序集“Enyim.Caching”或它的某一个依赖项。未能验证强名称签名

from:http://www.mzwu.com/article.asp?id=3741 itHub下载Enyim项目,编译后引用程序运行出错: 引用内容 未能加载文件或程序集“Enyim.Caching, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cec98615db04012e”或它的某一个依赖项.强名称验证失败. 既然是强名称的原因,不给程序集签名就好了.打开Enyim.Caching项目属性,不勾选为程序集签名: 编译引用,问题依旧

程序集生成失败 -- 引用的程序集“ThoughtWorks.QRCode”没有强名称,为没有源码的程序集强签名

进入sdk提示符界面,依次输入如下指令 sn -k ThoughtWorks.QRCode.snk ildasm ThoughtWorks.QRCode.dll /out=ThoughtWorks.QRCode.il ilasm ThoughtWorks.QRCode.il /dll /resource=ThoughtWorks.QRCode.res /key=ThoughtWorks.QRCode.snk /optimize sn -v ThoughtWorks.QRCode.dll

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

程序集强签名

1.特点 1.1.强签名的程序集可以注册到GAC(全局应用程序集缓存),不同的应用程序可以共享同一个dll. 1.2.强签名的库(应用程序)只能引用强签名的库.非强签名的库(应用程序)没有限制,既可以引用强签名的库,也可以引用非强签名的库.(实际测试,强签名的应用只引用非强签名的程序集但不使用是可以的,只要使用了引入库的东西,那就会报:引用的程序集没有强签名) 1.3.强签名无法保护源代码,但能防止dll被第三方篡改. 1.4.能防止dll冲突. 2.方法 --如果有源代码 项目 -> 右键 -

强名称程序集(strong name assembly)——为程序集赋予强名称

引言: 在以前的项目开发中,在程序集中见到过一个后缀为*.snk的文件,当时看这个文件的图标,感觉可能是企业内部保护版权啥的一种方式. 一,强程序集解决了哪些问题? 1,唯一标识一个程序集 2,放置程序集被仿冒和被篡改. 3,可以部署到全局程序集缓存(GAC:GlobalAssembly Cache)中:在将强名称程序集不熟在GAC当中以后,强名称程序集也可以称为共享程序集(shared assembly) 二,强名称与GAC 强名称: 定义了一个规则,不单单以文件名来区分程序集,这个规则就是强