Tags: .NET


本文将探讨数字签名、数字证书、强签名程序集、反编译等以及它们在.NET中的运用(一些概念并不局限于.NET在其它技术、平台中也存在)。

1.数字签名

数字签名又称为公钥数字签名,或者电子签章等,它借助公钥加密技术实现。数字签名技术主要涉及公钥、私钥、非对称加密算法。

1.1公钥与私钥

公钥是公开的钥匙,私钥则是与公钥匹配的严格保护的私有密钥;私钥加密的信息只有公钥可以解开,反之亦然。

在Visual Studio中,可以在项目属性页或者通过强命名工具Sn.exe生成公钥与私钥对,参考创建公钥私钥对

1.2非对称加密算法

非对称加密算法(也叫公钥加密算法)借助于私钥加密、公钥解密,使用了两个不同的密钥,因此被称为非对称加密;数字签名属于非对称加密算法的一种。比较著名的非对称加密算法是RSA,在.NET中对应的类为RSACryptoServiceProvider类,参看http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rsacryptoserviceprovider.aspx 。数字加密算法包括密码生成算法、密码标记算法、验证算法。NET中的RSACryptoServiceProvider类包含这些相应的方法。

1.3数字签名过程

消息的发送者用一个HASH函数提取出消息的摘要,然后用私钥借助非对称加密算法对消息进行加密,将消息原文与加密后的数字签名同时发送给接受者;消息接受者收到消息原文与数字签名后,首先利用同一HASH函数从消息原文提取消息摘要,再用发送者发布的公钥对数字签名进行解密出发送者的消息摘要,对比消息摘要,如果一致则证明,此消息来自发送者,且内容未经修改。

对于RSA算法,解密与解密分别对应:EncryptDecrypt方法。

1.4数字签名的功能

确定消息确实是由发送方签名并发出来的,因为私钥是私有的,其他人假冒不了发送方的签名。二是数字签名能确定消息的完整性。因为数字签名的特点是它代表了消息的特征,消息如果发生改变,HASH函数得到的消息摘要就会不同,数字签名的值也将发生变化,因而不同的消息将得到不同的数字签名。

2.数字证书

数字证书亦称电子证书,它是在Internet上用来标志和证明网络通信双方身份的数字信息文件。它是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。授权中心的数字签名可以帮助我们从授权中心验证证书是否被更改,即身份信息是否被更改等。简言之它就如同我们的身份证。

数字证书可以保证信息除发送方和接收方外不被其它人窃取;信息在传输过程中不被篡改;发送方能够通过数字证书来确认接收方的身份;发送方对于自己的信息不能抵赖。

数字证书广泛运用于服务器证书,如SSL证书,电子邮件证书,客户端证书等等。

理论上任何人都可以给你发个数字证书,亦即是说给你发数字证书的那个人或机构对你的公钥进行加签。对PGP和GPG系统来说,就是如此,而不需要一个统一的身份认证机构。但我们一般仅信任权威的数字认证机构颁发的证书。

在Internet Properties->内容->证书 可以查看已经安装在本机的证书:


上图选中项为阿里巴巴的证书的公钥。

3.强签名程序集

3.1强签名程序集简介

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

强名称依赖于唯一的密钥对来确保名称的唯一性,只有强名称的程序集可以加入到GAC中;强名称保护程序集的版本沿袭;强名称提供可靠的完整性检查。强名称就如同证书一样,帮助两个程序集之间建立信任关系。

3.3强名称程序集的实现方案

请参看:

强名称方案

ClickOnce 应用程序的强名称签名

对应用程序和部署清单进行签名

对程序集进行签名

3.4延迟程序集签名

为程序集签名时,您可能不会始终具有对私钥的访问权限。在这种情况下,可以使用“延迟签名”或“部分签名”来提供公钥,从而将私钥的添加推迟到交付程序集时。参考http://msdn.microsoft.com/zh-cn/library/9fas12zx%28v=VS.90%29.aspx

4.强签名程序集实践

4.1新建测试项目StrongNameAssembly,为其添加一个类TestClass

namespace StrongNameAssembly

{
    public class TestClass
    {
    }

}
4.2在项目属性的签名选项卡设置强签名

 生成项目后,用.NET Reflector打开程序集,我们可以发现,程序集的的名称包含了公钥标志:

// Assembly StrongNameAssembly, Version 1.0.0.0
Location:D:\Raymond's Documents\Visual Studio 2005\Projects\StrongNameAssembly\StrongNameAssembly\bin\Debug\StrongNameAssembly.dll
Name:StrongNameAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=79dbc45bc8054239
Type:Library

如果不进行强签名,那么公钥标志为null。

4.3新建测试项目ConsoleApplication1

添加对StrongNameAssembly的引用,并且更改Program.cs内容为:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write(new StrongNameAssembly.TestClass().ToString());
            Console.ReadLine();
        }
    }
}

生成解决方案,用Reflector打开控制台应用程序,查看其Reference中包含了对StrongNameAssembly的完整引用:

// Assembly Reference StrongNameAssembly
Version:1.0.0.0
Name:StrongNameAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=79dbc45bc8054239

 运行解决方案:


4.4更改强签名程序集的密钥为新的签名密钥或者取消强签名而不改变ConsoleApplication1

随意修改了强签名的密钥,查看到公钥标识变为

// Assembly StrongNameAssembly, Version 1.0.0.0
Location:D:\Raymond's Documents\Visual Studio 2005\Projects\StrongNameAssembly\StrongNameAssembly\bin\Debug\StrongNameAssembly.dll
Name:StrongNameAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03b8e071332d29f
Type:Library

 直接将程序集复制到ConsoleApplication1运行目录下,如Debug目录下,运行会得到错误提示,未能加载程序集trongNameAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=79dbc45bc8054239

4.5修改ConsoleApplication1.exe的应用程序清单

用Reflector或者IL DASM可以查看到ConsoleApplication1.exe的清单Manifest中,引用StrongNameAssembly的公钥标识依然为之前的版本公钥标识:

.assemblyexternStrongNameAssembly
{
.ver 1:0:0:0
.publickeytoken = (79DBC45BC8054239)
}

如果我们修改此标识为新的密钥签名的公钥标识(b03b8e071332d29f)会是怎样呢?

用IL DASM打开ConsoleApplication1.exe,转储为.il文件,用记事本打开:


红色框注部分则为需要修改的地方,修改为:

.assembly extern /*23000002*/ StrongNameAssembly
{
  .publickeytoken = (b0 3b 8e 07 13 32 d2 9f )                         // y..[..B9
  .ver 1:0:0:0
}

保存,并且用重新编译为应用程序(参考MSIL汇编程序):

 ilasm /resource=ConsoleApplication1.res ConsoleApplication1.IL /exe
@pause


如上图编译成功。

运行也成功:


在Reflector中也可以看到修改后的引用:

// Assembly Reference StrongNameAssembly
Version:1.0.0.0
Name:StrongNameAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03b8e071332d29f

由此可以看到,通过强名称对应用程序集进行保护是很脆弱的,或者说没有太大的用途;往往我们还需要借助代码混淆来实现源码保护,而此话题不在本文讨论范围之内

5.几点补充

在强命名方案一文中,有提到开发环境或工具使用开发人员私钥对包含程序集清单的文件哈希签名。该数字签名存储在包含程序集的清单的可移植可执行 (PE) 文件中。这一点通过IL DASM可以查看其公钥、HASH算法、以及公钥标志:

.assembly extern mscorlib
{
 .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}

.publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00   // .$..............
                00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00   // .$..RSA1........
                63 AE 3B 85 F4 22 94 FF 18 F8 7C 9D 4F 74 79 CA   // c.;.."....|.Oty.
                21 32 78 FA CD 10 E7 F3 68 D8 00 97 C4 CF 4D F8   // !2x.....h.....M.
                E1 00 2D E8 55 B5 51 E8 BA 91 82 9D 3E DA 61 8A   // ..-.U.Q.....>.a.
                7B 4E A0 FB 2D 1F BF 5F 7D 17 9A A1 3A A4 2C F7   // {N..-.._}...:.,.
                30 FA BC 73 ED 39 B1 64 B9 15 74 E7 25 89 8D B8   // 0..s.9.d..t.%...
                07 94 C5 78 E5 2C 22 C6 A2 8D D2 64 4D 2B 18 B2   // ...x.,"....dM+..
                39 26 18 DE 33 71 8E B5 87 C1 59 E5 2F 83 A0 8C   // 9&..3q....Y./...
                A5 EF B9 FC 83 12 D8 0A 1D 6A 91 BD 3D 81 1F D3 ) // .........j..=...
  .hash algorithm 0x00008004
  .ver 1:0:0:0
而以上代码为IL格式即为EXE或者DLL格式的PE文件反编译为IL后的内容,它需要在我们的.NET环境CLR中才可以被解释执行;在其CLR Header中也定义了.NET的版本:

// ----- CLR Header:
// Header size:                        0x00000048
// Major runtime version:              0x0002
// Minor runtime version:              0x0005
// 0x00002074 [0x00000558] address [size] of Metadata Directory:        
// Flags:                              0x00000001
// Entry point token:                  0x06000001
// 0x00000000 [0x00000000] address [size] of Resources Directory:       
// 0x00000000 [0x00000000] address [size] of Strong Name Signature:     
// 0x00000000 [0x00000000] address [size] of CodeManager Table:         
// 0x00000000 [0x00000000] address [size] of VTableFixups Directory:    
// 0x00000000 [0x00000000] address [size] of Export Address Table:      
// 0x00000000 [0x00000000] address [size] of Precompile Header:   

关于.NET PE结构请参考 The .NET File Format,本文不涉及此内容。

 

About author
Disclaimer
The opinions and comments expressed herein are my own personal opinions and do not represent my employer's view in any way.
Comments
No comments.
Add comment
Title
Title is required.
Name
Name is required.
Email
Please input your personal email with valid format.
Comments
Please input comment content.
Captcha Refresh
Input captcha:

Subscription

Statistics

Locations of visitors to this page