.NET中加密解密的支援
相信通過前面幾頁的敘述,大家已經明白了加密解密、數位簽章的基本原理,下面我們看一下在.NET中是如何來支援加密解密的。正如上面我們所進行的分類,.NET中也提供了兩組類用於加密解密,一組為對稱式加密,一組為非對稱式加密。這些類按照名稱還可以分為兩組,一組尾碼為“CryptoServiceProvider”的,是對於底層Windows API的封裝類,一組尾碼為“Managed”,是在.NET中全新編寫的類。
.NET對稱式加密解密支援
.NET Framework提供了一些常見的對稱演算法的實現,包括DES、RC2等。下面列出了與對稱演算法有關的類的結構:
System.Security.Cryptography.SymmetricAlgorithm
System.Security.Cryptography.Aes
System.Security.Cryptography.AesCryptoServiceProvider
System.Security.Cryptography.AesManaged
System.Security.Cryptography.DES
System.Security.Cryptography.DESCryptoServiceProvider
System.Security.Cryptography.RC2
System.Security.Cryptography.RC2CryptoServiceProvider
System.Security.Cryptography.Rijndael
System.Security.Cryptography.RijndaelManaged
System.Security.Cryptography.TripleDES
System.Security.Cryptography.TripleDESCryptoServiceProvider
這些類中黑體表示的類為實現演算法的具體類,其它的都是抽象類別;也可以根據需要實現自己的對稱演算法。
.NET非對稱式加密解密支援
RSA是當今最常用的非對稱演算法,其被應用於很多領域。RSA演算法的理論依據來自於一個大素數所具有的特性:對於給定的兩個大素數A與B,很容易計算出它們的乘積;但是,僅知道AB的成績卻很難計算原來的A與B各自的值。
.NET Frameork提供了兩個類供我們使用RSA演算法:用於加密資料的RSACryptoServiceProvider類以及用於對資料做數位簽章的DSACryptoServiceProvider類(DSA: Digital Signature Algorithm,數位簽章演算法),其類的階層如下
System.Security.Cryptography.AsymmetricAlgorithm
System.Security.Cryptography.DSA
System.Security.Cryptography.DSACryptoServiceProvider
System.Security.Cryptography.RSA
System.Security.Cryptography.RSACryptoServiceProvider
System.Security.Cryptography.ECDiffieHellman(橢圓曲線Diffie-Hellman (ECDH) 演算法實現)
System.Security.Cryptography.ECDiffieHellmanCng(橢圓曲線Diffie-Hellman (ECDH) 演算法的新一代密碼編譯 (CNG) 實現)
System.Security.Cryptography.ECDsa
System.Security.Cryptography.ECDsaCng(橢圓曲線數位簽章演算法 (ECDSA) 的新一代密碼編譯 (CNG) 實現)
.NET散列演算法支援
散列演算法是把長長的一列資料變成較短的代碼,這是最流行的64位散列密鑰。兩個最流行的散列演算法是SHA(Secured Hash Algorithm)和MD5(MessageDigest version5)。這些散列密鑰用於標記數字文檔。
散列值是根據一個資料集合計算出來的數字。如果資料集合不同,那麼計算出來的數字基本上也是不同的。.NET Framework在其System.Security.Cryptography命名空間下提供了一些主要的散列演算法,包括:SHAx、RIPEMD160、與MD5。
System.Security.Cryptography.HashAlgorithm
System.Security.Cryptography.KeyedHashAlgorithm(鍵控雜湊演算法的實現基類)
System.Security.Cryptography.HMAC (基於雜湊的訊息驗證碼 (HMAC)的抽象類別)
System.Security.Cryptography.HMACMD5
System.Security.Cryptography.HMACRIPEMD160
System.Security.Cryptography.HMACSHA1
System.Security.Cryptography.HMACSHA256
System.Security.Cryptography.HMACSHA384
System.Security.Cryptography.HMACSHA512
System.Security.Cryptography.MACTripleDES
System.Security.Cryptography.MD5
System.Security.Cryptography.MD5Cng(128 位雜湊演算法的 CNG(新一代密碼編譯)實現)
System.Security.Cryptography.MD5CryptoServiceProvider
System.Security.Cryptography.RIPEMD160
System.Security.Cryptography.RIPEMD160Managed
System.Security.Cryptography.SHA1
System.Security.Cryptography.SHA1CryptoServiceProvider
System.Security.Cryptography.SHA1Managed
System.Security.Cryptography.SHA256
System.Security.Cryptography.SHA256Managed
System.Security.Cryptography.SHA384
System.Security.Cryptography.SHA384Managed
System.Security.Cryptography.SHA512
System.Security.Cryptography.SHA512Managed
KeyedHash
鍵控雜湊演算法是依賴於密鑰的單向雜湊函數,用作訊息驗證碼。只有知道密鑰的人才能驗證雜湊值。加密雜湊演算法提供沒有機密的真實性。
SHA
SHA(安全散列演算法)是一個塊密碼,在64位的資料區塊上執行,這個演算法的改進版本採用了更大的密鑰值,當然,密鑰值越大所需的計算時間越長。而且,對於相對較小的檔案,散列值越小就越安全,就是說,散列演算法的塊大小應小於或等於資料區塊本身的大小。 SHA1演算法的散列範圍是160位。
.NET Framework也提供了更大的密鑰值演算法,分別為SHA256、SHA384和SHA512。名稱最後的數字表示其塊大小。
ASP.NET安全性中的成員資格提供者使用SHA1加密使用者密碼。
MD5
MD5表示MessageDigest版本5。它是一個加密的單向散列演算法。MD5有抗偽造,計算成本低且執行簡單等特點。現在MD5已成為散列演算法的事實標準。
.NET Framework提供了MD5CryptoServiceProvider這個類實現MD5演算法。該類與SHA1共用同一個基類,前面的樣本也只需要做簡單的改動即可成為使用MD5進行處理的例子。
RIPEMD-160
基於MD5的RIPEMD-160最早出現於歐洲,是一個使用160位的散列演算法。.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。它的建構函式如下,共有三個參數:
publicCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
5. 當加密時,stream為密文流(注意此時密文流還沒有包含資料,僅僅是一個空流);ICryptoTransform是第3步建立的加密器,包含著加密的演算法;CryptoStreamMode枚舉為Write,意思是將流經CryptoStream的明文流寫入到密文流中。最後,從密文流中獲得加密後的資料。
6. 當解密時,stream為密文流(此時密文流含有資料);ICryptoTransform是第3步建立的解密器,包含著解密的演算法;CryptoStreamMode枚舉為Read,意思是將密文流中的資料讀出到byte[]數組中,進而再由byte[]轉換為明文流、明文字串。
可見,CryptoStream總是接受密文流,並且根據CryptoStreamMode枚舉的值來決定是將明文流寫入到密文流(加密),還是將密文流讀入到明文流中(解密)。下面是我編寫的一個加密解密的Helper類:
// 對稱式加密協助類 public class CryptoHelper { private SymmetricAlgorithm provider; private ICryptoTransform encryptor; private ICryptoTransform decryptor; private const int BufferSize = 1024; public CryptoHelper(string algorithmName) { provider = SymmetricAlgorithm.Create(algorithmName); string strKey = ConfigurationManager.AppSettings["CryptoKey"]; string strIV = ConfigurationManager.AppSettings["CryptoIV"]; if (string.IsNullOrEmpty(strKey) || string.IsNullOrEmpty(strIV)) throw new ArgumentNullException("CryptoKey"); //Key byte[] bsKey = Encoding.UTF8.GetBytes(strKey); int keySize = provider.KeySize/8; if (bsKey.Length != keySize) { byte[] key = new byte[keySize]; if (bsKey.Length > keySize) Array.Copy(bsKey, key, keySize); else Array.Copy(bsKey, key, bsKey.Length); provider.Key = key; } else { provider.Key = bsKey; } //IV byte[] bsIV = Encoding.UTF8.GetBytes(strIV); int ivSize = provider.BlockSize / 8; if (bsIV.Length != ivSize) { byte[] iv = new byte[ivSize]; if(bsIV.Length > ivSize) Array.Copy(bsIV, iv, ivSize); else Array.Copy(bsIV, iv, bsIV.Length); provider.IV = iv; } else { provider.IV = bsIV; } encryptor = provider.CreateEncryptor(); decryptor = provider.CreateDecryptor(); } public CryptoHelper() : this("TripleDES") { } // Encrypt public string Encrypt(string clearText) { byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText); MemoryStream clearStream = null; MemoryStream encryptedStream = null; CryptoStream cryptoStream = null; try { clearStream = new MemoryStream(clearBuffer); encryptedStream = new MemoryStream(); cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write); byte[] buffer = new byte[BufferSize]; int bytesRead = 0; while((bytesRead = clearStream.Read(buffer, 0, BufferSize)) > 0) { cryptoStream.Write(buffer, 0, bytesRead); } cryptoStream.FlushFinalBlock(); buffer = encryptedStream.ToArray(); string encryptedText = Convert.ToBase64String(buffer); return encryptedText; } finally { if (cryptoStream != null) { cryptoStream.Close(); cryptoStream = null; } if (encryptedStream != null) { encryptedStream.Close(); encryptedStream = null; } if (clearStream != null) { clearStream.Close(); clearStream = null; } if (provider != null) { provider.Clear(); } } } // Decrypt public string Decrypt(string encryptedText) { byte[] encryptedBuffer = Convert.FromBase64String(encryptedText); byte[] buffer = new byte[BufferSize]; Stream encryptedStream = null; MemoryStream clearStream = null; CryptoStream cryptoStream = null; try { encryptedStream = new MemoryStream(encryptedBuffer); clearStream = new MemoryStream(); cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read); int bytesRead = 0; while ((bytesRead = cryptoStream.Read(buffer, 0, BufferSize)) > 0) { clearStream.Write(buffer, 0, bytesRead); } buffer = clearStream.GetBuffer(); string clearText = Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length); return clearText; } finally { if (encryptedStream != null) { encryptedStream.Close(); encryptedStream = null; } if (clearStream != null) { clearStream.Close(); clearStream = null; } if (cryptoStream != null) { cryptoStream.Close(); cryptoStream = null; } if (provider != null) { provider.Clear(); } } } }
非對稱式加密解密樣本
如果對檔案的安全性要求不是很嚴格,只是控制檔案在傳輸中的完整性,可以用散列演算法算出一個檔案的散列,然後用非對稱演算法加密並把加密後的資訊附加於檔案中,接收方使用收到的檔案中的散列驗證檔案的完整性,同時也能確認檔案的寄件者,即實現了簡單的檔案簽名的功能。
下面樣本示範了如何使用RSACryptoServiceProvider類加密一個字串。其中,ExpertParameter(bool)方法根據參數的真假允許獲得公開金鑰/私密金鑰對(true)或只有公開金鑰(false)。
using System;using System.Text;using System.Security.Cryptography;class Program{ static void Main() { string sMsg = "The message to encrypt!"; string sEnc, sDec; Encoding utf = new UTF8Encoding(); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); RSAParameters publicKey = rsa.ExportParameters(false); RSAParameters publicAndPrivateKey = rsa.ExportParameters(true); { RSACryptoServiceProvider rsaEncryptor = new RSACryptoServiceProvider(); rsaEncryptor.ImportParameters(publicKey); byte[] bMsg = utf.GetBytes(sMsg); byte[] bEnc = rsaEncryptor.Encrypt(bMsg, false); sEnc = Convert.ToBase64String(bEnc); } { RSACryptoServiceProvider rsaDecryptor = new RSACryptoServiceProvider(); rsaDecryptor.ImportParameters(publicAndPrivateKey); byte[] bEnc = Convert.FromBase64String(sEnc); byte[] bDec = rsaDecryptor.Decrypt(bEnc, false); sDec = utf.GetString(bDec); } Console.WriteLine("Message : " + sMsg); Console.WriteLine("Encrypted: " + sEnc); Console.WriteLine("Decrypted: " + sDec); }}
雜湊計算樣本
以下以MD5演算法計算雜湊值為例,給出簡單樣本。
public string MD5Crypto(string input) { MD5 md5Hasher = null; try { // 建立? MD5CryptoServiceProvider 對象U的I執行個體a md5Hasher = MD5.Create(); //計算Z哈u希o代a碼 byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); StringBuilder sBuilder = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("X2")); } return sBuilder.ToString(); } finally { if (md5Hasher != null) { md5Hasher.Clear(); md5Hasher.Dispose(); md5Hasher = null; } } }