.Net中的加密解密

转 .Net中的加密解密
  • 发布时间: 2015/11/23 12:55
  •  
  • 阅读: 33
  •  
  • 收藏: 3
  •  
  • 点赞: 0
  •  
  • 评论: 0

在一些比较重要的应用场景中,经过网络传递数据须要进行加密以保证安全。本文将简单地介绍了加密解密的一些概念,以及相关的数字签名、证书,最后介绍了如何在.NET中对数据进行对称加密和解密。html

加密和解密

说 到加密,可能你们最熟悉的就是MD5了,记得几年前我刚开始接触Web编程的时候,研究的一个ASP论坛程序,它的用户密码就是采用的MD5进行加密。 MD5实际上只是一种散列运算,或者能够称为单向的加密,便是说没法根据密文(加密后的数据),推导出明文(原数据)。而咱们下面要说明的,是在加密后可 以进行解密、还原数据的。对于欲进行加密的对象,有的人称为消息,有的人称为数据,有的人称为信息,为了不混淆,在本文后面部分,我统一将其称为消息。那么加密是什么呢?加密是经过对消息进行编码,创建一种安全的交流方式,使得只有你和你所指望的接收者可以理解。算法

那么怎么样才能叫安全呢?消息在接收方和发送方进行安全传递,通常要知足下面三个要点:编程

  1. 消息的发送方可以肯定消息只有预期的接收方能够解密(不保证第三方没法得到,但保证第三方没法解密)。
  2. 消息的接收方能够肯定消息是由谁发送的(消息的接收方能够肯定消息的发送方)。
  3. 消息的接收方能够肯定消息在途中没有被篡改过(必须确认消息的完整性)。

加密一般分为两种方式:对称加密和非对称加密,接下来咱们先看看对称加密。数组

对称加密

对称加密的思路很是简单,就是含有一个称为密钥的东西,在消息发送前使用密钥对消息进行加密,在对方收到消息以后,使用相同的密钥进行解密。根据密钥来产生加密后的消息(密文)的这一加工过程,由加密算法来完成,加密算法一般是公开的。它的流程以下:安全

  1. 发送方使用密钥对消息进行加密。
  2. 接收方使用一样的密钥对消息进行解密。

可使用下面一副图来表示:网络

.Net中的加密解密

对称加密存在这样两个问题:数据结构

  1. 虽然能够经过密钥来保证消息安全地进行传递,可是如何确保密钥安全地进行传递?由于发送者和接收者总有一次初始的通讯,用来传递密钥,此时的安全如何保证?
  2. 接收者虽然能够根据密钥来解密消息,但由于存在上面的问题,消息有多是由第三方(非法得到密钥)发来的,而接收方没法辨别。

为了解决上面两个问题,就须要介绍一下非对称加密。并发

非对称加密

非对称加密的接收者和发送者都持有两个密钥,一个是对外公开的,称为公钥,一个是自行保管的,称为私钥。非对称加密的规则是由某人A的公钥加密的消息,只能由A的私钥进行解密;由A的私钥加密的消息只能由A的公钥解密。此时咱们能够得出接收方、发送方有两个公钥两个私钥一共四个密钥,咱们先看看两种简单的方式,这两种方式都是只使用两个密钥。ide

第一种模式只使用接收方的公钥和私钥,称为加密模式。函数

加密模式

在加密模式中,由消息的接收方发布公钥,持有私钥。好比发送方要发送消息“hello,jimmy”到接收方,它的步骤是:

  1. 发送方使用接收者的公钥进行加密消息,而后发送。
  2. 接收方使用本身的私钥对消息进行解密。

可使用下面一幅图来描述:

.Net中的加密解密

在这种模式下,若是第三方截获了发送者发出的消息,由于他没有接收者的私钥,因此这个消息对他来讲毫无心义。可见,它可以知足本文最开始提出的消息安全传递的要点一:消息的发送方可以肯定消息只有预期的接收方能够解密(不保证第三方没法得到,但保证第三方没法解密)。

除此之外,由于接收方的公钥是公开的,任何人均可以使用这个公钥来加密消息并发往接收者,而接收者没法对消息进行判别,没法知道是由谁发送来的。因此,它不知足咱们开始提出的消息安全传递的要点二:消息的接收方能够肯定消息是由谁发送的(消息的接收方能够肯定消息的发送方)。

这个问题能够在下面的认证模式中获得解决。

认证模式

在认证模式中,由消息的发送方发布公钥,持有私钥。好比发送者要发送消息“Welcome to Tracefact.net”到接收者,它的步骤是:

  1. 发送者使用本身的私钥对消息进行加密,而后发送。
  2. 接收者使用发送者的公钥对消息进行解密。

能够用下面一副图来表述:

.Net中的加密解密

在 这种模式下,假如发送方叫作Ken,接收方叫作Matthew,由于Matthew只能使用Ken的公钥对消息进行解密,而没法使用Molly、 Sandy或者任何其余人公钥对消息进行解密,因此他必定可以肯定消息是由Ken发送来的。所以,这个模式知足了前面提出的消息安全传递的要点二。

与此同时,由于Ken的公钥是公开的,任何截获了该消息的第三方都可以使用Ken的公钥来对消息进行解密,换言之,消息如今是不安全的。所以,与加密模式正好相反,它没法知足前面提出的消息安全传递的要点一。

而不论是采用加密模式仍是认证模式,都没有解决加密解密中的要点三:接收方必须可以确认消息没有被改动过。为了解决这个问题,又引入了数字签名。

数字签名

基本实现

数 字签名实际上就是上面非对称加密时的认证模式,只不过作了一点点的改进,加入了散列算法。你们比较熟悉的散列算法可能就是MD5了,不少开源论坛都采用 了这个算法。散列算法有三个特色:一是不可逆的,由结果没法推算出原数据;二是原数据哪怕是一丁点儿的变化,都会使散列值产生巨大的变化;三是不论多么大 或者多么少的数据,总会产生固定长度的散列值(常见的为32位64位)。产生的散列值一般称为消息的摘要(digest)。

那么如何经过引入散列函数来保证数据的完整性呢?也就是接收方可以确认消息确实是由发送方发来的,而没有在中途被修改过。具体的过程以下:

  1. 发送方将想要进行传递的消息进行一个散列运算,获得消息摘要。
  2. 发送方使用本身的私钥对摘要进行加密,将消息和加密后的摘要发送给接收方。
  3. 接收方使用发送方的公钥对消息和消息摘要进行解密(确认了发送方)。
  4. 接收方对收到的消息进行散列运算,获得一个消息摘要。
  5. 接收方将上一步得到的消息摘要与发送方发来的消息摘要进行对比。若是相同,说明消息没有被改动过;若是不一样,说明消息已经被篡改。

这个过程能够用下面的一副图来表述:

.Net中的加密解密

咱们能够看出,数字签名经过引入散列算法,将非对称加密的认证模式又增强了一步,确保了消息的完整性。除此之外,注意到上面的非对称加密算法,只是对消息摘要进行了加密,而没有对消息自己进行加密。非对称加密是一个很是耗时的操做,因为只对消息摘要加密,使得运算量大幅减小,因此这样可以显著地提升程序的执行速度。同时,它依然没有确保消息不被第三方截获到,不只如此,由于此时消息是以明文进行传递,第三方甚至不须要发送方的公钥,就能够直接查看消息。

为了解决这样的问题,只须要将非对称加密的认证模式、加密模式以及消息摘要进行一个结合就能够了,这也就是下面的高级模式。

高级实现

因为这个过程比上面稍微复杂了一些,咱们将其分为发送方和接收方两部分来看。先看看发送方须要执行的步骤:

  1. 将消息进行散列运算,获得消息摘要。
  2. 使用本身的私钥对消息摘要加密(认证模式:确保了接收方可以确认本身)。
  3. 使用接收方的公钥对消息进行加密(加密模式:确保了消息只能由指望的接收方解密)。
  4. 发送消息和消息摘要。

接下来咱们看一下接收方所执行的步骤:

  1. 使用发送方的公钥对消息摘要进行解密(确认了消息是由谁发送的)。
  2. 使用本身的私钥对消息进行解密(安全地得到了实际应得到的信息)。
  3. 将消息进行散列运算,得到消息摘要。
  4. 将上一步得到的消息摘要 和 第一步解密的消息摘要进行对比(确认了消息是否被篡改)。

可 以看到,经过上面这种方式,使用了接收方、发送方所有的四个密钥,再配合使用消息摘要,使得前面提出的安全传递的全部三个条件全都知足了。那么是否是这 种方法就是最好的呢?不是的,由于咱们已经说过了,非对称加密是一种很耗时的操做,因此这个方案是很低效的。实际上,咱们能够经过它来解决对称加密中的密 钥传递问题,若是你已经忘记了能够翻到前面再看一看,也就是说,咱们可使用这里的高级实现方式来进行对称加密中密钥的传递,对于以后实际的数据传递,采 用对称加密方式来完成,由于此时已是安全的了。

证书机制

与 数字签名相关的一个概念就是证书机制了,证书是用来作什么呢?在上面的各类模式中,咱们一直使用了这样一个假设,就是接收方或者发送方所持有的、对方的 公钥老是正确的(确实是对方公布的)。而实际上除非对方手把手将公钥交给咱们,不然若是不采起措施,双方在网络中传递公钥时,同样有可能被篡改。那么怎样 解决这个问题呢?这时就须要证书机制了:能够引入一个公正的第三方,当某一方想要发布公钥时,它将自身的身份信息及公钥提交给这个第三方,第三方对其身份进行证明,若是没有问题,则将其信息和公钥打包成为证书(Certificate)。而这个公正的第三方,就是常说的证书颁发机构(Certificate Authority)。当咱们须要获取公钥时,只须要得到其证书,而后从中提取出公钥就能够了。

.NET中加密解密的支持

对称加密和解密

相信经过前面几页的叙述,你们已经明白了加密解密、数字签名的基本原理,下面咱们看一下在.NET中是如何来支持加密解密的。正如上面咱们所进行的分类,.NET中也提供了两组类用于加密解密,一组为对称加密,一组为非对称加密,以下图所示:

.Net中的加密解密

上面的类按照名称还能够分为两组,一组后缀为“CryptoServiceProvider”的,是对于底层Windows API的包装类,一组后缀为“Managed”,是在.NET中全新编写的类。如今假设咱们以TripleDES做为算法,那么加密的流程以下:

  1. 先建立一个TripleDESCryptoServiceProvider的实例,实例名好比叫provider。
  2. 在 provider上指定密钥和IV,也就是它的Key属性和IV属性。这里简单解释一下IV(initialization vector),若是一个字符串(或者数据)加密以前不少部分是重复的好比ABCABCABC,那么加密以后尽管字符串是乱码,但相关部分也是重复的。为 了解决这个问题,就引入了IV,当使用它之后,加密以后即便是重复的也被打乱了。对于特定算法,密钥和IV的值能够随意指定,但长度是固定,一般密钥为 128位或196位,IV为64位。密钥和IV都是byte[]类型,所以,若是使用Encoding类来将字符串转换为byte[],那么编码方式就很 重要,由于UTF8是变长编码,因此对于中文和英文,须要特别注意byte[]的长度问题。
  3. 如 果是加密,在provider上调用CreateEncryptor()方法,建立一个ICryptoTransform类型的加密器对象;若是是解密, 在provider上调用CreateDecryptor()方法,一样是建立一个ICryptoTransform类型的解密器对象。 ICryptoTransform定义了加密转换的运算,.NET将在底层调用这个接口。
  4. 因 为流和byte[]是数据类型无关的一种数据结构,能够保存和传输任何形式的数据,区别只是byte[]是一个静态的概念而流是一个动态的概念。因 此,.NET采用了流的方式进行加密和解密,咱们能够想到有两个流,一个是明文流,含有加密前的数据;一个是密文流,含有加密后的数据。那么就必然有一个中介者,将明文流转换为密文流;或者将密文流转换为明文流。.NET中执行这个操做的中介者也是一个流类型,叫作CryptoStream。它的构造函数以下,共有三个参数:

    public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)

  5. 当加密时,stream 为密文流(注意此时密文流尚未包含数据,仅仅是一个空流);ICryptoTransform是第3步建立的加密器,包含着加密的算 法;CryptoStreamMode枚举为Write,意思是将流经CryptoStream的明文流写入到密文流中。最后,从密文流中得到加密后的数 据。
  6. 当解密 时,stream为密文流(此时密文流含有数据);ICryptoTransform是第3步建立的解密器,包含着解密的算 法;CryptoStreamMode枚举为Read,意思是将密文流中的数据读出到byte[]数组中,进而再由byte[]转换为明文流、明文字符 串。

可见,CryptoStream老是接受密文流,而且根据CryptoStreamMode枚举的值来决定是将明文流写入到密文流(加密),仍是将密文流读入到明文流中(解密)。下面是我编写的一个加密解密的Helper类:

 1 public class CryptoHelper
 2     {
 3 
 4         // 对称加密算法提供器
 5         private ICryptoTransform encryptor;    // 加密器对象
 6         private ICryptoTransform decryptor;    // 解密器对象
 7         private const int BufferSize = 1024;
 8 
 9         public CryptoHelper(string algorithmName, string key)
10         {
11             SymmetricAlgorithm provider = SymmetricAlgorithm.Create(algorithmName);
12             provider.Key = Encoding.UTF8.GetBytes(key);
13             provider.IV = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
14 
15             encryptor = provider.CreateEncryptor();
16             decryptor = provider.CreateDecryptor();
17         }
18 
19         public CryptoHelper(string key) : this("TripleDES", key) { }
20 
21         // 加密算法
22         public string Encrypt(string clearText)
23         {
24             // 建立明文流
25             byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText);
26             MemoryStream clearStream = new MemoryStream(clearBuffer);
27 
28             // 建立空的密文流
29             MemoryStream encryptedStream = new MemoryStream();
30 
31             CryptoStream cryptoStream =
32                 new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write);
33 
34             // 将明文流写入到buffer中
35             // 将buffer中的数据写入到cryptoStream中
36             int bytesRead = 0;
37             byte[] buffer = new byte[BufferSize];
38             do
39             {
40                 bytesRead = clearStream.Read(buffer, 0, BufferSize);
41                 cryptoStream.Write(buffer, 0, bytesRead);
42             } while (bytesRead > 0);
43 
44             cryptoStream.FlushFinalBlock();
45 
46             // 获取加密后的文本
47             buffer = encryptedStream.ToArray();
48             string encryptedText = Convert.ToBase64String(buffer);
49             return encryptedText;
50         }
51 
52         // 解密算法
53         public string Decrypt(string encryptedText)
54         {
55             byte[] encryptedBuffer = Convert.FromBase64String(encryptedText);
56             Stream encryptedStream = new MemoryStream(encryptedBuffer);
57 
58             MemoryStream clearStream = new MemoryStream();
59             CryptoStream cryptoStream =
60                 new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
61 
62             int bytesRead = 0;
63             byte[] buffer = new byte[BufferSize];
64 
65             do
66             {
67                 bytesRead = cryptoStream.Read(buffer, 0, BufferSize);
68                 clearStream.Write(buffer, 0, bytesRead);
69             } while (bytesRead > 0);
70 
71             buffer = clearStream.GetBuffer();
72             string clearText =
73                 Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length);
74 
75             return clearText;
76         }
77 
78         public static string Encrypt(string clearText, string key)
79         {
80             CryptoHelper helper = new CryptoHelper(key);
81             return helper.Encrypt(clearText);
82         }
83 
84         public static string Decrypt(string encryptedText, string key)
85         {
86             CryptoHelper helper = new CryptoHelper(key);
87             return helper.Decrypt(encryptedText);
88         }
89     }

这个类进行一个简单的测试:

string key = "ABCDEFGHIJKLMNOP";
            string clearText = "欢迎访问www.tracefact.net";

            CryptoHelper helper = new CryptoHelper(key);

            string encryptedText = helper.Encrypt(clearText);
            Console.WriteLine(encryptedText);

            clearText = CryptoHelper.Decrypt(encryptedText, key);
            Console.WriteLine(clearText);
           
            Console.ReadKey(true);

转载自:http://my.oschina.net/lichaoqiang/blog/534173

相关文章
相关标签/搜索