Welcome

首页 / 软件开发 / .NET编程技术 / 强名称(1)使用强名称保护代码完整性

强名称(1)使用强名称保护代码完整性2014-07-03强名称是由程序集的标识加上公钥和数字签名组成的。其中,程序集的标识包括简单文本名称、版本号和区域性信息(如果提供的话)。强名称是使用相应的私钥,通过程序集文件(包含程序集清单的文件,并因而也包含构成该程序集的所有文件的名称和散列)生成的。Microsoft Visual Studio .NET 和在 .NET Framework SDK 中提供的其他开发工具能够将强名称分配给一个程序集。强名称相同的程序集应该是相同的。

通过签发具有强名称的程序集可以确保名称的全局唯一性。强名称满足以下要求:

1) 强名称依赖于唯一的密钥对来确保名称的唯一性。程序集名称不会与任何其他程序集名称相同,因为用一个私钥生成的程序集的名称与用其他私钥生成的程序集的名称不相同。

2) 强名称保护程序集的版本沿袭。强名称可以确保没有人能够生成程序集的后续版本。用户可以确信,他们所加载的程序集的版本出自创建该版本(应用程序是用该版本生成的)的同一个发行者。

强名称提供可靠的完整性检查。通过.NET Framework 安全检查后,即可确信程序集的内容在生成后未被更改过。但请注意,强名称中(或强名称本身)并不暗含信任级别,例如由数字签名和支持证书提供的信任。使用强名称保护代码完整性虽然强名称是.NET加密领域的元老,也是微软推荐的应用程序保护机制,但是由于托管程序可以被反汇编成IL代码,更改或者去除强名称也就成了可能。强名称的保护强度也因此大大减弱。

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

在.NET平台下区分程序集采用的方法和Win32一样,使用名称,但是名称有强弱之分。弱名称包含版本号、程序集名称和文化。强名称在弱名称的基础上添加了数字签名。强名称的作用主要有三个:

q  区分不同的程序集;

q  确保代码没有被篡改过;

q  在.NET中,只有强名称签名的程序集才能放到全局程序集缓存中。

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

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

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

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

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

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

图9-8生成密钥文件

接下来新建一个控制台测试项目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();}}}