1. 程式人生 > >用RSA加密實現Web登入密碼加密傳輸

用RSA加密實現Web登入密碼加密傳輸

通常我們做一個Web應用程式的時候都需要登入,登入就要輸入使用者名稱和登入密碼,並且,使用者名稱和登入密碼都是明文傳輸的,這樣就有可能在中途被別人攔截,尤其是在網咖等場合。

這裡順帶一個小插曲,我以前有家公司,辦公室裝修時候安排的網口相對較少,不太夠用,於是我和另外一個同事使用了一個hub來共享一個網口,這就導致了很有趣的現象:任何他的網路包我都能抓得到,當然了,我的他也能抓得到。這是不是有很大的安全隱患了?我有可能在不經意間會洩漏自己的密碼。

所以,很多安全要求較高的網站都不會明文傳輸密碼,它們會使用https來確保傳輸過程的安全,https是用證書來實現的,證書來自於證書頒發機構,當然了,你也可以自己造一張證書,但這樣別人訪問你的網站的時候還是會遇到麻煩,因為你自己造的證書不在使用者瀏覽器的信任範圍之內,你還得在使用者瀏覽器上安裝你的證書,來讓使用者瀏覽器相信你的網站,很多使用者並不知道如何操作,就算會操作,也能也不樂意幹;另一種選擇是你向權威證書頒發機構申請一張證書,但這樣有一定的門檻,還需要付費,也不是我們樂意乾的事。

所以,我打算自己實現一個密碼加密傳輸方法。

這裡使用了RSA非對稱加密演算法,對稱加密也許大家都已經很熟悉,也就是加密和解密用的都是同樣的金鑰,沒有金鑰,就無法解密,這是對稱加密。而非對稱加密演算法中,加密所用的金鑰和解密所用的金鑰是不相同的:你使用我的公鑰加密,我使用我的私鑰來解密;如果你不使用我的公鑰加密,那我無法解密;如果我沒有私鑰,我也沒法解密。

我設計的這個登入密碼加密傳輸方法的原理圖如下:

首先,先演練一下非對稱加密:

static void Main(string[] args)
{
    //用於字串和byte[]之間的互轉
    UTF8Encoding utf8encoder = new UTF8Encoding();

    //產生一對公鑰私鑰
    RSACryptoServiceProvider rsaKeyGenerator = new RSACryptoServiceProvider(1024);
    string publickey = rsaKeyGenerator.ToXmlString(false);
    string privatekey = rsaKeyGenerator.ToXmlString(true);
            
    //使用公鑰加密密碼
    RSACryptoServiceProvider rsaToEncrypt = new RSACryptoServiceProvider();
    rsaToEncrypt.FromXmlString(publickey);
    string strPassword = "@123#abc$";
    Console.WriteLine("The original password is: {0}", strPassword);
    byte[] byEncrypted = rsaToEncrypt.Encrypt(utf8encoder.GetBytes(strPassword), false);
    Console.Write("Encoded bytes: ");
    foreach (Byte b in byEncrypted)
    {
        Console.Write("{0}", b.ToString("X"));
    }
    Console.Write("\n");
    Console.WriteLine("The encrypted code length is: {0}", byEncrypted.Length);

    //解密
    RSACryptoServiceProvider rsaToDecrypt = new RSACryptoServiceProvider();
    rsaToDecrypt.FromXmlString(privatekey);
    byte[] byDecrypted = rsaToDecrypt.Decrypt(byEncrypted, false);
    string strDecryptedPwd = utf8encoder.GetString(byDecrypted);
    Console.WriteLine("Decrypted Password is: {0}", strDecryptedPwd);
}

大家可以清楚看到,密碼被加密成128位元組長度的密文,為什麼是固定128位元組呢?這是因為我們的RSACryptoServiceProvider預設生成的key的長度是1024,即1024位的加密,所以不管你要加密的密碼有多長,它生成的密文的長度肯定是128位元組,也因為這樣,密碼的長度是有限制的,1024位的RSA演算法,只能加密大約100個位元組長度的明文,要提高可加密的明文的長度限制,就得增加key的長度,比如把key改到2048位,這樣能加密的明文的長度限制也就變為大概200出頭這樣……還是太少啊!而且這樣會帶來加密速度的顯著下降,RSA本來就很慢……是的,比同沒有長度限制的對稱加密,這種非對稱加密的限制可真多,即便是200個字元,又能傳輸什麼東西呢?——密碼!這個就夠了,傳輸完密碼之後,我們就使用對稱加密,所以,RSA往往是用來“協商”一個對稱加密的key的。

接下去,真正的難點在於用javascript實現一個和.net的RSA相容的演算法。密碼學,對我來說真像天書一般,每次我一看就頭大,這個工作是沒辦法自己做的了,只能到網上找,那是相當的費力啊,找到許多js的RSA實現,但都和.net的這套東西不相容,最後還是功夫不負有心人,終於找到了一套。不多說,上程式碼:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>RSA Login Test</title>
    <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script src="Scripts/jQuery.md5.js" type="text/javascript" ></script>
    <script src="Scripts/BigInt.js" type="text/javascript"></script>
    <script src="Scripts/RSA.js" type="text/javascript"></script>
    <script src="Scripts/Barrett.js" type="text/javascript"></script>
    <script type="text/javascript">
        function cmdEncrypt() {
            setMaxDigits(129);
            var key = new RSAKeyPair("<%=strPublicKeyExponent%>", "", "<%=strPublicKeyModulus%>");
            var pwdMD5Twice = $.md5($.md5($("#txtPassword").attr("value")));
            var pwdRtn = encryptedString(key, pwdMD5Twice);
            $("#encrypted_pwd").attr("value", pwdRtn);
            $("#formLogin").submit();
            return;
        }
    </script>

</head>
<body>
    <form action="Default.aspx" id="formLogin" method="post">
    <div>
        <div>
            User Name:
        </div>
        <div>
            <input id="txtUserName" name="txtUserName" value="<%=postbackUserName%>" type="text" maxlength="16" />
        </div>
        <div>
            Password:
        </div>
        <div>
            <input id="txtPassword" type="password" maxlength="16" />
        </div>
        <div>
            <input id="btnLogin" type="button" value="Login" onclick="return cmdEncrypt()" />
        </div>
    </div>
    <div>
        <input type="hidden" name="encrypted_pwd" id="encrypted_pwd" />
    </div>
    </form>
    <div>
        <%=LoginResult%>
    </div>
</body>
</html>

這是客戶端程式碼,大家可以看到,基本沒有什麼伺服器端程式碼,<%=postbackUserName%>用於回顯輸入的使用者名稱,&lt;%=LoginResult%>用於顯示登入結果,<%=strPublicKeyExponent%>和&lt;%=strPublicKeyModulus%>則用來告訴客戶端RSA公鑰。需要的javascript檔案說明:

  • jQuery.md5.js -  用於對密碼進行兩次md5加密;(我通常在資料庫中儲存的使用者密碼是兩次MD5後的結果)
  • BigInt.js - 用於生成一個大整型;(這是RSA演算法的需要)
  • RSA.js - RSA的主要演算法;
  • Barrett.js - RSA演算法所需要用到的一個支援檔案;

對於密碼學,我幾乎一無所知,所以沒辦法跟大家解釋清楚RSA演算法的原理,抱歉,我只知道怎麼用。關於javascript中這行程式碼:“setMaxDigits(129);”具體表示什麼我也不清楚,我只知道,把引數改為小於129的數之後會導致客戶端的javascript執行進入死迴圈。伺服器端程式碼也很簡單:

protected void Page_Load(object sender, EventArgs e)
{
    LoginResult = "";
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    if (string.Compare(Request.RequestType, "get", true)==0)
    {
        //將私鑰存Session中
        Session["private_key"] = rsa.ToXmlString(true);
    }
    else
    {
        bool bLoginSucceed = false;
        try
        {
            string strUserName = Request.Form["txtUserName"];
            postbackUserName = strUserName;
            string strPwdToDecrypt = Request.Form["encrypted_pwd"];
            rsa.FromXmlString((string)Session["private_key"]);
            byte[] result = rsa.Decrypt(HexStringToBytes(strPwdToDecrypt), false);
            System.Text.ASCIIEncoding enc = new ASCIIEncoding();
            string strPwdMD5 = enc.GetString(result);
            if (string.Compare(strUserName, "user1", true)==0 && string.Compare(strPwdMD5, "14e1b600b1fd579f47433b88e8d85291", true)==0)
                bLoginSucceed = true;
        }
        catch (Exception)
        {

        }
        if (bLoginSucceed)
            LoginResult = "登入成功";
        else
            LoginResult = "登入失敗";
    }

    //把公鑰適當轉換,準備發往客戶端
    RSAParameters parameter = rsa.ExportParameters(true);
    strPublicKeyExponent = BytesToHexString(parameter.Exponent);
    strPublicKeyModulus = BytesToHexString(parameter.Modulus);
}

使用者名稱“user1”
密碼“123456”

登入成功!

抓取http報文看看POST的“密碼”:

這樣的“密碼”的破解就成為了理論上的可行了。:)

下面提供完整程式碼下載(使用VS2010開發環境):

http://files.cnblogs.com/guogangj/RSALoginTest.zip

原文:http://www.cnblogs.com/AloneSword/archive/2013/09/18/3329359.html