在一些比較重要的應用場景中,經過網絡傳遞數據須要進行加密以保證安全。本文將簡單地介紹了加密解密的一些概念,以及相關的數字簽名、證書,最後介紹瞭如何在.NET中對數據進行對稱加密和解密。html
說 到加密,可能你們最熟悉的就是MD5了,記得幾年前我剛開始接觸Web編程的時候,研究的一個ASP論壇程序,它的用戶密碼就是採用的MD5進行加密。 MD5實際上只是一種散列運算,或者能夠稱爲單向的加密,便是說沒法根據密文(加密後的數據),推導出明文(原數據)。而咱們下面要說明的,是在加密後可 以進行解密、還原數據的。對於欲進行加密的對象,有的人稱爲消息,有的人稱爲數據,有的人稱爲信息,爲了不混淆,在本文後面部分,我統一將其稱爲消息。那麼加密是什麼呢?加密是經過對消息進行編碼,創建一種安全的交流方式,使得只有你和你所指望的接收者可以理解。算法
那麼怎麼樣才能叫安全呢?消息在接收方和發送方進行安全傳遞,通常要知足下面三個要點:編程
加密一般分爲兩種方式:對稱加密和非對稱加密,接下來咱們先看看對稱加密。數組
對稱加密的思路很是簡單,就是含有一個稱爲密鑰的東西,在消息發送前使用密鑰對消息進行加密,在對方收到消息以後,使用相同的密鑰進行解密。根據密鑰來產生加密後的消息(密文)的這一加工過程,由加密算法來完成,加密算法一般是公開的。它的流程以下:安全
可使用下面一副圖來表示:網絡
對稱加密存在這樣兩個問題:數據結構
爲了解決上面兩個問題,就須要介紹一下非對稱加密。併發
非對稱加密的接收者和發送者都持有兩個密鑰,一個是對外公開的,稱爲公鑰,一個是自行保管的,稱爲私鑰。非對稱加密的規則是由某人A的公鑰加密的消息,只能由A的私鑰進行解密;由A的私鑰加密的消息只能由A的公鑰解密。此時咱們能夠得出接收方、發送方有兩個公鑰兩個私鑰一共四個密鑰,咱們先看看兩種簡單的方式,這兩種方式都是隻使用兩個密鑰。ide
第一種模式只使用接收方的公鑰和私鑰,稱爲加密模式。函數
在加密模式中,由消息的接收方發佈公鑰,持有私鑰。好比發送方要發送消息「hello,jimmy」到接收方,它的步驟是:
可使用下面一幅圖來描述:
在這種模式下,若是第三方截獲了發送者發出的消息,由於他沒有接收者的私鑰,因此這個消息對他來講毫無心義。可見,它可以知足本文最開始提出的消息安全傳遞的要點一:消息的發送方可以肯定消息只有預期的接收方能夠解密(不保證第三方沒法得到,但保證第三方沒法解密)。
除此之外,由於接收方的公鑰是公開的,任何人均可以使用這個公鑰來加密消息併發往接收者,而接收者沒法對消息進行判別,沒法知道是由誰發送來的。因此,它不知足咱們開始提出的消息安全傳遞的要點二:消息的接收方能夠肯定消息是由誰發送的(消息的接收方能夠肯定消息的發送方)。
這個問題能夠在下面的認證模式中獲得解決。
在認證模式中,由消息的發送方發佈公鑰,持有私鑰。好比發送者要發送消息「Welcome to Tracefact.net」到接收者,它的步驟是:
能夠用下面一副圖來表述:
在 這種模式下,假如發送方叫作Ken,接收方叫作Matthew,由於Matthew只能使用Ken的公鑰對消息進行解密,而沒法使用Molly、 Sandy或者任何其餘人公鑰對消息進行解密,因此他必定可以肯定消息是由Ken發送來的。所以,這個模式知足了前面提出的消息安全傳遞的要點二。
與此同時,由於Ken的公鑰是公開的,任何截獲了該消息的第三方都可以使用Ken的公鑰來對消息進行解密,換言之,消息如今是不安全的。所以,與加密模式正好相反,它沒法知足前面提出的消息安全傳遞的要點一。
而不論是採用加密模式仍是認證模式,都沒有解決加密解密中的要點三:接收方必須可以確認消息沒有被改動過。爲了解決這個問題,又引入了數字簽名。
數 字簽名實際上就是上面非對稱加密時的認證模式,只不過作了一點點的改進,加入了散列算法。你們比較熟悉的散列算法可能就是MD5了,不少開源論壇都採用 了這個算法。散列算法有三個特色:一是不可逆的,由結果沒法推算出原數據;二是原數據哪怕是一丁點兒的變化,都會使散列值產生巨大的變化;三是不論多麼大 或者多麼少的數據,總會產生固定長度的散列值(常見的爲32位64位)。產生的散列值一般稱爲消息的摘要(digest)。
那麼如何經過引入散列函數來保證數據的完整性呢?也就是接收方可以確認消息確實是由發送方發來的,而沒有在中途被修改過。具體的過程以下:
這個過程能夠用下面的一副圖來表述:
咱們能夠看出,數字簽名經過引入散列算法,將非對稱加密的認證模式又增強了一步,確保了消息的完整性。除此之外,注意到上面的非對稱加密算法,只是對消息摘要進行了加密,而沒有對消息自己進行加密。非對稱加密是一個很是耗時的操做,因爲只對消息摘要加密,使得運算量大幅減小,因此這樣可以顯著地提升程序的執行速度。同時,它依然沒有確保消息不被第三方截獲到,不只如此,由於此時消息是以明文進行傳遞,第三方甚至不須要發送方的公鑰,就能夠直接查看消息。
爲了解決這樣的問題,只須要將非對稱加密的認證模式、加密模式以及消息摘要進行一個結合就能夠了,這也就是下面的高級模式。
因爲這個過程比上面稍微複雜了一些,咱們將其分爲發送方和接收方兩部分來看。先看看發送方須要執行的步驟:
接下來咱們看一下接收方所執行的步驟:
可 以看到,經過上面這種方式,使用了接收方、發送方所有的四個密鑰,再配合使用消息摘要,使得前面提出的安全傳遞的全部三個條件全都知足了。那麼是否是這 種方法就是最好的呢?不是的,由於咱們已經說過了,非對稱加密是一種很耗時的操做,因此這個方案是很低效的。實際上,咱們能夠經過它來解決對稱加密中的密 鑰傳遞問題,若是你已經忘記了能夠翻到前面再看一看,也就是說,咱們可使用這裏的高級實現方式來進行對稱加密中密鑰的傳遞,對於以後實際的數據傳遞,採 用對稱加密方式來完成,由於此時已是安全的了。
與 數字簽名相關的一個概念就是證書機制了,證書是用來作什麼呢?在上面的各類模式中,咱們一直使用了這樣一個假設,就是接收方或者發送方所持有的、對方的 公鑰老是正確的(確實是對方公佈的)。而實際上除非對方手把手將公鑰交給咱們,不然若是不採起措施,雙方在網絡中傳遞公鑰時,同樣有可能被篡改。那麼怎樣 解決這個問題呢?這時就須要證書機制了:能夠引入一個公正的第三方,當某一方想要發佈公鑰時,它將自身的身份信息及公鑰提交給這個第三方,第三方對其身份進行證明,若是沒有問題,則將其信息和公鑰打包成爲證書(Certificate)。而這個公正的第三方,就是常說的證書頒發機構(Certificate Authority)。當咱們須要獲取公鑰時,只須要得到其證書,而後從中提取出公鑰就能夠了。
相信經過前面幾頁的敘述,你們已經明白了加密解密、數字簽名的基本原理,下面咱們看一下在.NET中是如何來支持加密解密的。正如上面咱們所進行的分類,.NET中也提供了兩組類用於加密解密,一組爲對稱加密,一組爲非對稱加密,以下圖所示:
上面的類按照名稱還能夠分爲兩組,一組後綴爲「CryptoServiceProvider」的,是對於底層Windows API的包裝類,一組後綴爲「Managed」,是在.NET中全新編寫的類。如今假設咱們以TripleDES做爲算法,那麼加密的流程以下:
public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
可見,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