去年寫過一篇博文《Web應(yīng)用的安全的登錄認(rèn)證》,使用HMAC的加密算法保證了在登錄時(shí)的密碼安全。雖然沒看到有人質(zhì)疑,但這里至少會(huì)引出一個(gè)問題:登錄過程不需要將密碼原文提交到服務(wù)器,但注冊(cè)和修改密碼這兩個(gè)需要將密碼原文提交到服務(wù)器的過程怎么辦?
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了蓬江免費(fèi)建站歡迎大家使用!
解決這一問題最佳方案當(dāng)然是使用非對(duì)稱加密。簡單的說,非對(duì)稱加密算法需要兩個(gè)密鑰,分別稱為公鑰和私鑰,其中公鑰會(huì)被公布出來,而私鑰由個(gè)了保管(就像保管自己的密碼一樣)。使用公鑰加密的數(shù)據(jù)是不能用公鑰解密的,只能由私鑰來解密。如果將私鑰保存在服務(wù)器,把公鑰發(fā)送給瀏覽器對(duì)密碼原文進(jìn)行加密,那么加密后的數(shù)據(jù)在傳輸過程中是安全的,因?yàn)樗借€始終不會(huì)出現(xiàn)在傳輸過程,這個(gè)加密數(shù)據(jù)就不能輕松的解開。關(guān)于非對(duì)稱加密的知識(shí),學(xué)霸們請(qǐng)去各種百科上搜索,這里就不多說了。
目前最常用的非對(duì)稱加密算法是RSA算法,在服務(wù)器端.NET、Java都支持,PHP也有組件支持。在瀏覽器端,也有JavaScript的RSA算法包——用Google搜索“javascript rsa”第一個(gè)結(jié)果就是“RSA In JavaScript - ohdave.com”,該網(wǎng)站提供了三個(gè)JS文件,BitInt.js、Barrett.js和RSA.js,這三個(gè)JS文件要在見面中按順序引用。
下面就以C#和JavaScript為例說明一下加密傳輸密碼和后臺(tái)解密的過程。
當(dāng)然第一步是要產(chǎn)生公鑰和私鑰,自己用C#寫個(gè)小小的控制臺(tái)程序或者Windows程序就能解決這個(gè)問題,順便熟悉一下C#的RSA。順便說一下,維基百科提到,要保證安全至少得使用1024位的Key,.NET的RSA支持384到2048位的Key,這里就以1024位為例吧,下面的程序會(huì)在執(zhí)行目錄輸出一個(gè)key.xml文件,保存了產(chǎn)生的公鑰和私鑰
[STAThread] static void Main(string[] args) { string keyFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "key.xml"; const int keySize = 1024; // 這個(gè)類在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可以看到,這個(gè)Key值包含了如下幾個(gè)部分:Modulus、Exponent、P、Q、DP、DQ、InverseQ、D。這幾個(gè)部分的值都是以Base64編碼保存的。其中,Modulus和Exponent就是組成公鑰的部分,也就是需要傳遞給瀏覽器,簡稱M和E,用于加密的兩個(gè)數(shù)值。
生成key.xml之后,有兩種方式使用,一種是在使用時(shí)從XML文件導(dǎo)入;另一種方式是把這幾個(gè)值提取出來寫在某個(gè)類中。導(dǎo)入XML需要先將XML文本讀取到一個(gè)string中,再用RSACryptoServiceProvider的實(shí)例方法FromXmlString(string)導(dǎo)入。因?yàn)樾枰驗(yàn)g覽器提供M和E,所以還要把這兩個(gè)值提取出來
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(xmlString); RSAParameters rsap = rsa.ExportParameters(false); //下面就是需要的兩個(gè)值 //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里,我個(gè)人比較喜歡把一些小數(shù)據(jù)寫在<HEAD>標(biāo)簽的屬性中,就像這樣:<HEAD M="..." E="...">。C#代碼也很簡單:
Header.Attributes["M"] = rsap.Modulus.HexEncode(); Header.Attributes["E"] = rsap.Exponent.HexEncode();
這里用到了一個(gè)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對(duì)象,這個(gè)對(duì)象的構(gòu)造函數(shù)定義在RSA.js中。不過在new RSAKeyPair之前,必須先調(diào)用setMaxDigits()函數(shù),原因在BigInt.js中有說明,setMaxDigits()的參數(shù)值根據(jù)選用的RSA的Key大小不同,如果計(jì)算我不太清楚,不過按ohdave.com的示例(下載頁面的源碼就是示例),1024位的Key,應(yīng)該設(shè)置setMaxDigits(130);如果是2048位的則應(yīng)該設(shè)置為260。所以產(chǎn)生RSAKeyPair對(duì)象的代碼應(yīng)該是這樣:
$(function() { var key = (function() { var m = $("head").attr("M"); var e = $("head").attr("E"); setMaxDigits(130); // 第一個(gè)參數(shù)是加密因子,第二個(gè)參數(shù)是解密因子 // 因?yàn)闉g覽器端不需要解密,所以第二個(gè)參數(shù)傳入空字符串 return new RSAKeyPair(e, "", m); })(); })
在提交數(shù)據(jù)之前對(duì)密碼進(jìn)行加密
var encryptedPass = encryptedString(key, $("#password").val());
提交到后臺(tái)之后,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算法加密哦。后面有示例下載。
新聞標(biāo)題:Web應(yīng)用中保證密碼傳輸安全
文章源于:http://vcdvsql.cn/article6/jhjjig.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、網(wǎng)站營銷、網(wǎng)站收錄、網(wǎng)站內(nèi)鏈、外貿(mào)網(wǎng)站建設(shè)、Google
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)