溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Web應(yīng)用中保證密碼傳輸安全

發(fā)布時間:2020-07-10 21:25:43 來源:網(wǎng)絡(luò) 閱讀:6444 作者:邊城__ 欄目:web開發(fā)

  去年寫過一篇博文《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算法加密哦。后面有示例下載。


附件:http://down.51cto.com/data/2364078
向AI問一下細(xì)節(jié)

免責(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)容。

AI