.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

相關文章
相關標籤/搜索