您好,登錄后才能下訂單哦!
去年寫過一篇博文《Web應(yīng)用的安全的登錄認(rèn)證》,使用HMAC的加密算法保證了在登錄時的密碼安全。雖然沒看到有人質(zhì)疑,但這里至少會引出一個問題:登錄過程不需要將密碼原文提交到服務(wù)器,但注冊和修改密碼這兩個需要將密碼原文提交到服務(wù)器的過程怎么辦?
解決這一問題最佳方案當(dāng)然是使用非對稱加密。簡單的說,非對稱加密算法需要兩個密鑰,分別稱為公鑰和私鑰,其中公鑰會被公布出來,而私鑰由個了保管(就像保管自己的密碼一樣)。使用公鑰加密的數(shù)據(jù)是不能用公鑰解密的,只能由私鑰來解密。如果將私鑰保存在服務(wù)器,把公鑰發(fā)送給瀏覽器對密碼原文進(jìn)行加密,那么加密后的數(shù)據(jù)在傳輸過程中是安全的,因為私鑰始終不會出現(xiàn)在傳輸過程,這個加密數(shù)據(jù)就不能輕松的解開。關(guān)于非對稱加密的知識,學(xué)霸們請去各種百科上搜索,這里就不多說了。
目前最常用的非對稱加密算法是RSA算法,在服務(wù)器端.NET、Java都支持,PHP也有組件支持。在瀏覽器端,也有JavaScript的RSA算法包——用Google搜索“javascript rsa”第一個結(jié)果就是“RSA In JavaScript - ohdave.com”,該網(wǎng)站提供了三個JS文件,BitInt.js、Barrett.js和RSA.js,這三個JS文件要在見面中按順序引用。
下面就以C#和JavaScript為例說明一下加密傳輸密碼和后臺解密的過程。
當(dāng)然第一步是要產(chǎn)生公鑰和私鑰,自己用C#寫個小小的控制臺程序或者Windows程序就能解決這個問題,順便熟悉一下C#的RSA。順便說一下,維基百科提到,要保證安全至少得使用1024位的Key,.NET的RSA支持384到2048位的Key,這里就以1024位為例吧,下面的程序會在執(zhí)行目錄輸出一個key.xml文件,保存了產(chǎn)生的公鑰和私鑰
[STAThread] static void Main(string[] args) { string keyFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "key.xml"; const int keySize = 1024; // 這個類在System.Security.Cryptography命名空間中 RSACryptoServiceProvider sp = new RSACryptoServiceProvider(keySize); // 參數(shù)true表示XML中包含私鑰。如果給false表示只生成公鑰的XML string str = sp.ToXmlString(true); using (TextWriter writer = new StreamWriter(keyFileName)) { writer.Write(str); } }
打開生成的key.xml可以看到,這個Key值包含了如下幾個部分:Modulus、Exponent、P、Q、DP、DQ、InverseQ、D。這幾個部分的值都是以Base64編碼保存的。其中,Modulus和Exponent就是組成公鑰的部分,也就是需要傳遞給瀏覽器,簡稱M和E,用于加密的兩個數(shù)值。
生成key.xml之后,有兩種方式使用,一種是在使用時從XML文件導(dǎo)入;另一種方式是把這幾個值提取出來寫在某個類中。導(dǎo)入XML需要先將XML文本讀取到一個string中,再用RSACryptoServiceProvider的實(shí)例方法FromXmlString(string)導(dǎo)入。因為需要向瀏覽器提供M和E,所以還要把這兩個值提取出來
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(xmlString); RSAParameters rsap = rsa.ExportParameters(false); //下面就是需要的兩個值 //rsap.Exponent //rsap.Modulus
如果嫌每次載入費(fèi)事,可以這樣(其中所有字符串常量都是從key.xml中拷貝過來的)
private static readonly RSAParameters rsap = new RSAParameters { Modulus = Convert.FromBase64String(@"uQlRZvfH6MMdhNRgiAlKMY88dqsU2suKNIWbHY/FiTsvDgH5DLmNmGMp85qtQwSPhBQ+/E7DQkvk1OxIN7EBL+21NRPJIaDKuJciWC940ZFVU0d5oUujKy5uCrF/rfZce8MXjoiErtc+QRjCKI8wfGdIKuclooEPiJwb1rydMuE="), Exponent = Convert.FromBase64String(@"AQAB"), P = Convert.FromBase64String(@"wSwI9i+aM6h7hayvFD01iINAeZ9JK5qExBJAWDzjOQwWRE9x1dCX52jb+HrutwblfqQuOk6hazOmGTluxITXQw=="), Q = Convert.FromBase64String(@"9TfkbPTexGpQ9ZHNjYnmRJLcG8wG6yzzJ/RrWIjq1IKQYMhYDq08bNbUVuXlntKW9GgmEYnuhP8smrH5y+mRCw=="), DP = Convert.FromBase64String(@"s2Xx7LDIxLD0BnEZJ/KwhNdgSZNkoNof8vgASfJCE/jltQsS7T+L053OrDV+/PuqprJTPFNKFgUhfMuZ02iLgQ=="), DQ = Convert.FromBase64String(@"5IfLXXO0LI78lm/khlUPAbdwZIN3qzMABat3Y1Jur9BiZ6Au2LbASprH15h4r9WJE4wAdnX6kX4SfrUBHPW20w=="), InverseQ = Convert.FromBase64String(@"FhlNb2WkipUaXvuwDxEWPeE754+qM2F5otEUP9clG91yaerdsBpBmU0G6S2AqUNjr/qgfpQyl1EW2dl10rmTpw=="), D = Convert.FromBase64String(@"WjhPXv/Qks7T7UiqGppA+UIoToojPH1C0VIVrEfGHp/jVRakKs6sWhF7yoHwGf22xkUi4t26efBMTn84xSLCexjQwj5AQtYk+3Qr2QjRDdn2ooIV1gWKW/C0O0+80Y6PEeszItuBVfjKC6mNEcZ1g44/wOdvIG7Olsl0F7vmQrM=") };
將E和M傳遞給瀏覽器的方式,最直接就是寫在HTML里,我個人比較喜歡把一些小數(shù)據(jù)寫在<HEAD>標(biāo)簽的屬性中,就像這樣:<HEAD M="..." E="...">。C#代碼也很簡單:
Header.Attributes["M"] = rsap.Modulus.HexEncode(); Header.Attributes["E"] = rsap.Exponent.HexEncode();
這里用到了一個byte[]的擴(kuò)展方法HexEncode,即將byte[]轉(zhuǎn)換為16進(jìn)制字符串的方法,它最簡單(但不一定是最快)的實(shí)現(xiàn)方法是
public static string HexEncode(this byte[] me) { return BitConverter.ToString(me).Replace("-", string.Empty); }
現(xiàn)在是瀏覽器端的加密過程,引入需要的JS文件先:
<script type="text/javascript" src="js/BigInt.js"></script> <script type="text/javascript" src="js/Barrett.js"></script> <script type="text/javascript" src="js/RSA.js"></script> <script type="text/javascript" src="js/jquery-2.1.0.js"></script>
然后在加密密碼之前當(dāng)然要先得到RSAKeyPair對象,這個對象的構(gòu)造函數(shù)定義在RSA.js中。不過在new RSAKeyPair之前,必須先調(diào)用setMaxDigits()函數(shù),原因在BigInt.js中有說明,setMaxDigits()的參數(shù)值根據(jù)選用的RSA的Key大小不同,如果計算我不太清楚,不過按ohdave.com的示例(下載頁面的源碼就是示例),1024位的Key,應(yīng)該設(shè)置setMaxDigits(130);如果是2048位的則應(yīng)該設(shè)置為260。所以產(chǎn)生RSAKeyPair對象的代碼應(yīng)該是這樣:
$(function() { var key = (function() { var m = $("head").attr("M"); var e = $("head").attr("E"); setMaxDigits(130); // 第一個參數(shù)是加密因子,第二個參數(shù)是解密因子 // 因為瀏覽器端不需要解密,所以第二個參數(shù)傳入空字符串 return new RSAKeyPair(e, "", m); })(); })
在提交數(shù)據(jù)之前對密碼進(jìn)行加密
var encryptedPass = encryptedString(key, $("#password").val());
提交到后臺之后,C#解密的過程
string hex = Request["encrypted_pass"] byte[] data = hex.HexDecode(); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); // 前面定義的private static readonly RSAParameter rsap = ... rsa.ImportParameters(rsap); byte[] source = rsa.Decrypt(data, false); string password = Encodnig.ASCII.GetString(source);
密碼原文得到,剩下的事情就好說了,當(dāng)然是保存密碼,記得先使用HMAC算法加密哦。后面有示例下載。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。