所謂加密就是將數據進行不規則化以保證源數據機密性的機制或進行簽名以保證數據完整性。特別是如今電子商務的火熱和人們對隱私的注重,加密對於變通的程序員來說,也是必須考慮的問題了。如何不規則化數據呢,我們自己可能就會提出很多種方案,也就是一種加密算法,至於安全性可能就跟本身的設計有關了;而現如今就有很多公開的算法如DES、RSA、MD5等等,雖然算法是公開的,但是他們根據密鑰來加密,想要解密它們就需要解密的密鑰甚至有些是不能得到源數據的。那么.net提供哪些加密的方式呢?
我們常用的安全嗎
.net程序員最常用的肯定就是FormsAuthentication.HashPasswordForStoringInConfigFile,功能是根據指定的密碼和哈希算法生成一個適合於存儲在配置文件中的哈希密碼,當然哈希密碼要存儲在哪我們有絕對的控制權,大多數時候我們把用戶密碼加密之后,存儲到數據庫里。它需要兩個參數,一個是要加密的字體串,另一個是要使用的加密算法的字符串表示:Clear、MD5、SHA1,第一個表示不加密,后兩種是不同的加密算法。
這種算法是單向加密,就是說在你想從密文本身解出源數據幾乎是不可能的。好像很安全,其實不然。它是有弊端的:不管什么時候什么情況,對同一個數據加密得到的數據是一樣的。這本身沒什么啊,但是這其實是不安全,我只要建立足夠多的數據源數據和密文的對照庫,就非常可能從其中庫中查詢到源數據,成功不成功,安全取決於你這個庫的強大與否。如果你不信,可以在百度上搜MD5,會出現很多“MD5解密”的結果,隨便找一個試一下。。。
這種算法,也是我們下面將要提到的哈希加密算法,它從一段信息中產生一個哈希值,即使你更改其中任何一個字符,都會產生另外一個哈希值,因此它主要用於數據完整性的驗證,並不適合用於保存私密信息。
.NET的加密類
.NET的加密類分為三層,第一層是抽象的基類,用於完成加密任務,這些抽象類分別是:
AsymmetricAlgorithm:它提供非對稱的加密算法,就是解密加密分別要使用不同的密鑰完成。
SymmetricAlgorithm:相對於上一個,提供的對稱加密算法,解密加密使用的同一個密鑰。
HashAlgorithm:它提供是散列的生成算法和驗證機制。剛剛提到的MD5就屬於這個算法的范疇。
第二層代表一個指定的加密算法類,它們從加密基類繼承而來,但仍然是抽象類。如DES繼承自SymmetricAlgorithm,它代表DES算法。
第三層是一系列加密的實現,每一個實現類都是從一個加密算法繼承而來。同樣一個加密算法類可能有多個實現類。.net的加密類有的完全是在托管代碼中實現的,而有一些是對非托管的CryptoAPI庫的薄封裝,作為一個約定,封裝CryptoAPI的類它們的名字中都含有CryptoServiceProvider,而托管類在它們的名字都含有Managed。
對稱加密算法
從上圖可以看到,.net提供4種加密算法:DES (64)、TripleDES (128、192)、RC2 (40-128)、Rijndael (128、192、256),括號內數字是該算法有效密鑰長度。加密的強度和密鑰的長度有關,密鑰越長,攻擊都所要測試的數據就會越多,相對就越安全,但是同時也會使得加密解密的時間變長。對於大多數程序來說,Rijndael是一個較好的選擇,性能可靠又支持較長的密鑰。
對稱加密最大的好處是它的性能,但是它也存在以下一些問題:1,密鑰的交換:你必須選擇一種安全的方式交換密鑰;2,暴力破解:如果長時間使用一個密鑰,攻擊都就有可能有足夠的時間試出密鑰;3,密鑰的管理:你如果定期更換密鑰,交換密鑰及原來數據的支持都會是一個問題,還要雙方在一個安全的地方保存。
為了試圖解決這些問題,非對稱加密算法出現了。
非對稱加密算法
非對稱加密算法,需要兩個密鑰:一個加密密鑰,通常稱為公鑰;一個是解密密鑰,私鑰。通常我們把公鑰分發那些需要加密數據的人,而私鑰只有自己保存。用公鑰加密的數據只能用私鑰才能解密。
RSA,支持直接的加密和解密,有效密鑰長度384-16384。而RSA(數字簽名算法)只能用於對信息進行簽名和驗證簽名。
非對稱加密對公鑰完全公開,也就不存在對稱加密幾個問題,但是它的問題就是性能問題,如果在系統中多次交換數據或交換大量數據可能會影響系統性能。
優良方案(SSL的實現)
難道沒有一種較好的解決方案嗎,即兼顧性能,又安全?答案是肯定的,讓我們看一下SSL,我們知道SSL技術是http上交換的信息進行加密常用的,它被大量瀏覽器和服務器支持,用來保證之間傳輸的數據不會被輕易的竊聽或破解。
讓我們大概看一上它的實現方案,它使用了對稱加密和非對稱加密兩種方式:
從你輸入一個https的URl點擊enter鍵開始:
1,客戶端發送一個請求;
2,服務器會對自己的證書簽名然后發給客戶端,證書中包含非對稱加密的公鑰,私鑰當然自己保存;
3,客戶端驗證這個證書是自己信任的機構頒發的,然后驗證證書,如果驗證通過接受連接;
4,然后客戶端告訴服務自己支持什么類型的加密密鑰;
5,服務端選擇支持的最強的密鑰長度告訴客戶端;
6,在雙方協調完畢,瀏覽器根據指定的密鑰長度生成一個對稱加密的密鑰,瀏覽器用獲得的公鑰加密這個密鑰,把他傳給服務器;
7,服務器用自己保存的私鑰解密這個密鑰,獲取原始的對稱加密的密鑰,這樣雙方就都保存了這份對稱加密密鑰,那么在此次會話以后的通信中就會用這個密鑰加密和解密數據。
也就是說在一次會話中,只使用了一次非對稱加密,並且只是對一個密鑰加密解密,而其它多次大量數據的加密解密都是對稱加密來解決的,保證了性能;由於是瀏覽器端生成對稱加密密鑰並且采用非對稱加密算法加密,只有私鑰才能解密這個密鑰,保證對稱加密密鑰的交換的安全性;並且這個密鑰只在此次會話有效,從時間上減少了風險。
如何使用.net加密類
加密實例的創建
我們知道,加密類的第一層和第二層都是抽象的,抽象加密類它主要兩個好處:
1,定義加密實現類要支持的基本成員;
2,它們通過靜態的Create()方法,來直接創建一個實現類的實例。
例如:
DES des = DES.Create();
它返回了默認的DES算法的實現類,實際上它是DESCryptoServiceProvider實例,這樣做的優點就是面向對象的一些優點,如果某個時候,我們更改了DES的默認實現,但是我們這塊使用的代碼卻可以不做任何修改,只要這個實現仍然是從DES繼承而來。
另外各個算法除了實現加密和解密的功能之法,同時還提供了GenerateKey()方法:它會生成一個符合對應算法要求的密鑰。
加密實現
.net使用一個基於流的架構來實現加密和解密,這樣我們可以容易的加密或解密來自不同數據源不同類型的數據和多重加密。這其中最核心的類:ICryptoTransform接口和CryptoStream類。
ICryptoTransform定義了基於塊的轉換的操作:這可能是加密、解密、哈希、base64編碼、解碼或格式化操作。
DES des = DES.Create(); ICryptoTransform EnTransform = des.CreateEncryptor(); ICryptoTransform DeTransform = des.CreateDecryptor();
通過以上方法,我們可以基於一個加密類創建一個加密或解密的操作。各種加密任務都以相同的方式執行,但是每個加密操作在數據需要處理之前都被分為固定大小的塊,因此大多數時候我們選擇一個更簡單的方式:把它傳遞給另一個類:CrpytoStream。
CrpytoStream包裝一個流,並使用ICryptoTransform在后台執行它的工作。CrpytoStream使用緩沖訪問,因此可以自動加密而不用擔心算法所要求塊的大小;另外它可以封閉任何繼承於.net流的類如FileStream、MemoryStream或NetworkStream等,這樣我們很容易的在其它類操作之上使用。
創建CrpytoStream的實例,需要三個信息:底層的數據流、模式(讀或寫)、要使用的ICryptoTransform。
演示例子
/// <summary> /// 加密 /// </summary> /// <param name="data">要加密的數據</param> /// <returns></returns> public byte[] EncrytData(String data) { // 轉化為流使用的字節數組 byte[] byData = Encoding.UTF8.GetBytes(data); // 加密算法實例 DES des = DES.Create(); // 加密操作 ICryptoTransform EnTransform = des.CreateEncryptor(); // 生成密鑰,這里只是演示。密鑰應該之前生成,在此外使用或在此處生成然后以安全的方式保存、交換,以供解密用 des.GenerateKey(); byte[] cryptoKey = des.Key; // 加密的信息 MemoryStream stream = new MemoryStream(); // 創建加密流 CryptoStream cs = new CryptoStream(stream, EnTransform, CryptoStreamMode.Write); // 向stream寫入加密的信息 cs.Write(byData, 0, byData.Length); // 更新緩沖區狀態到基礎數據源 cs.FlushFinalBlock(); // 返回加密后的信息的字節數組形式 return stream.ToArray(); }
得到加密信息可以存儲或者發送給其它程序。
相對於加密,當然也要有解密:
/// <summary> /// 解密 /// </summary> /// <param name="data">要解密的數據</param> /// <param name="key">解密的密鑰,和上面的加密密鑰一定要是一樣的</param> /// <returns></returns> public String DecrytData(byte[] data,byte[] key) { // 算法實例並確定解密密鑰 DES des = DES.Create();
des.Key = key; // 解密操作 ICryptoTransform DeTransform = des.CreateDecryptor(); // 信息 MemoryStream stream = new MemoryStream(); // 創建流 CryptoStream cs = new CryptoStream(stream, DeTransform, CryptoStreamMode.Write); // 向stream寫入解密后的信息 cs.Write(data, 0, data.Length); // 更新緩沖區狀態到基礎數據源 cs.FlushFinalBlock(); // 返回解密后的字符串形式 return Encoding.UTF8.GetString(stream.ToArray()); }
這里只是一個簡單的例子,只是加密和解密的一些基本步驟,至於密鑰的交換和保存就看自己實際情況進行妥善處理了。
如何能讓系統更安全,這是開發者要最先考慮的問題,而加密是我們最常用也比較好用的保證安全的方式,我們應該對此盡可能的了解,然后才能做出優良的選擇和設計。