用RSA加密实现Web登录密码加密传输

通常我们做一个Web应用程序的时候都需要登录,登录就要输入用户名和登录密码,并且,用户名和登录密码都是明文传输的,这样就有可能在中途被别人拦截,尤其是在网吧等场合。

这里顺带一个小插曲,我以前有家公司,办公室装修时候安排的网口相对较少,不太够用,于是我和另外一个同事使用了一个hub来共享一个网口,这就导致了很有趣的现象:任何他的网络包我都能抓得到,当然了,我的他也能抓得到。这是不是有很大的安全隐患了?我有可能在不经意间会泄漏自己的密码。

所以,很多安全要求较高的网站都不会明文传输密码,它们会使用https来确保传输过程的安全,https是用证书来实现的,证书来自于证书颁发机构,当然了,你也可以自己造一张证书,但这样别人访问你的网站的时候还是会遇到麻烦,因为你自己造的证书不在用户浏览器的信任范围之内,你还得在用户浏览器上安装你的证书,来让用户浏览器相信你的网站,很多用户并不知道如何操作,就算会操作,也能也不乐意干;另一种选择是你向权威证书颁发机构申请一张证书,但这样有一定的门槛,还需要付费,也不是我们乐意干的事。

所以,我打算自己实现一个密码加密传输方法。

这里使用了RSA非对称加密算法,对称加密也许大家都已经很熟悉,也就是加密和解密用的都是同样的密钥,没有密钥,就无法解密,这是对称加密。而非对称加密算法中,加密所用的密钥和解密所用的密钥是不相同的:你使用我的公钥加密,我使用我的私钥来解密;如果你不使用我的公钥加密,那我无法解密;如果我没有私钥,我也没法解密。

我设计的这个登录密码加密传输方法的原理图如下:

 

首先,先演练一下非对称加密:

 

  1. static void Main(string[] args)  
  2. {  
  3.     //用于字符串和byte[]之间的互转  
  4.     UTF8Encoding utf8encoder = new UTF8Encoding();  
  5.   
  6.     //产生一对公钥私钥  
  7.     RSACryptoServiceProvider rsaKeyGenerator = new RSACryptoServiceProvider(1024);  
  8.     string publickey = rsaKeyGenerator.ToXmlString(false);  
  9.     string privatekey = rsaKeyGenerator.ToXmlString(true);  
  10.               
  11.     //使用公钥加密密码  
  12.     RSACryptoServiceProvider rsaToEncrypt = new RSACryptoServiceProvider();  
  13.     rsaToEncrypt.FromXmlString(publickey);  
  14.     string strPassword = "@123#abc$";  
  15.     Console.WriteLine("The original password is: {0}", strPassword);  
  16.     byte[] byEncrypted = rsaToEncrypt.Encrypt(utf8encoder.GetBytes(strPassword), false);  
  17.     Console.Write("Encoded bytes: ");  
  18.     foreach (Byte b in byEncrypted)  
  19.     {  
  20.         Console.Write("{0}", b.ToString("X"));  
  21.     }  
  22.     Console.Write("\n");  
  23.     Console.WriteLine("The encrypted code length is: {0}", byEncrypted.Length);  
  24.   
  25.     //解密  
  26.     RSACryptoServiceProvider rsaToDecrypt = new RSACryptoServiceProvider();  
  27.     rsaToDecrypt.FromXmlString(privatekey);  
  28.     byte[] byDecrypted = rsaToDecrypt.Decrypt(byEncrypted, false);  
  29.     string strDecryptedPwd = utf8encoder.GetString(byDecrypted);  
  30.     Console.WriteLine("Decrypted Password is: {0}", strDecryptedPwd);  
  31. }  

大家可以清楚看到,密码被加密成128字节长度的密文,为什么是固定128字节呢?这是因为我们的RSACryptoServiceProvider默认生成的key的长度是1024,即1024位的加密,所以不管你要加密的密码有多长,它生成的密文的长度肯定是128字节,也因为这样,密码的长度是有限制的,1024位的RSA算法,只能加密大约100个字节长度的明文,要提高可加密的明文的长度限制,就得增加key的长度,比如把key改到2048位,这样能加密的明文的长度限制也就变为大概200出头这样……还是太少啊!而且这样会带来加密速度的显著下降,RSA本来就很慢……是的,比同没有长度限制的对称加密,这种非对称加密的限制可真多,即便是200个字符,又能传输什么东西呢?——密码!这个就够了,传输完密码之后,我们就使用对称加密,所以,RSA往往是用来“协商”一个对称加密的key的。

接下去,真正的难点在于用javascript实现一个和.net的RSA兼容的算法。密码学,对我来说真像天书一般,每次我一看就头大,这个工作是没办法自己做的了,只能到网上找,那是相当的费力啊,找到许多js的RSA实现,但都和.net的这套东西不兼容,最后还是功夫不负有心人,终于找到了一套。不多说,上代码:


 

  1. <html xmlns="http://www.w3.org/1999/xhtml">  
  2. <head runat="server">  
  3.     <title>RSA Login Test</title>  
  4.     <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>  
  5.     <script src="Scripts/jQuery.md5.js" type="text/javascript" ></script>  
  6.     <script src="Scripts/BigInt.js" type="text/javascript"></script>  
  7.     <script src="Scripts/RSA.js" type="text/javascript"></script>  
  8.     <script src="Scripts/Barrett.js" type="text/javascript"></script>  
  9.     <script type="text/javascript">  
  10.         function cmdEncrypt() {  
  11.             setMaxDigits(129);  
  12.             var key = new RSAKeyPair("<%=strPublicKeyExponent%>", "", "<%=strPublicKeyModulus%>");  
  13.             var pwdMD5Twice = $.md5($.md5($("#txtPassword").attr("value")));  
  14.             var pwdRtn = encryptedString(key, pwdMD5Twice);  
  15.             $("#encrypted_pwd").attr("value", pwdRtn);  
  16.             $("#formLogin").submit();  
  17.             return;  
  18.         }  
  19.     </script>  
  20.   
  21. </head>  
  22. <body>  
  23.     <form action="Default.aspx" id="formLogin" method="post">  
  24.     <div>  
  25.         <div>  
  26.             User Name:  
  27.         </div>  
  28.         <div>  
  29.             <input id="txtUserName" name="txtUserName" value="<%=postbackUserName%>" type="text" maxlength="16" />  
  30.         </div>  
  31.         <div>  
  32.             Password:  
  33.         </div>  
  34.         <div>  
  35.             <input id="txtPassword" type="password" maxlength="16" />  
  36.         </div>  
  37.         <div>  
  38.             <input id="btnLogin" type="button" value="Login" onclick="return cmdEncrypt()" />  
  39.         </div>  
  40.     </div>  
  41.     <div>  
  42.         <input type="hidden" name="encrypted_pwd" id="encrypted_pwd" />  
  43.     </div>  
  44.     </form>  
  45.     <div>  
  46.         <%=LoginResult%>  
  47.     </div>  
  48. </body>  
  49. </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执行进入死循环。服务器端代码也很简单:

  1. protected void Page_Load(object sender, EventArgs e)  
  2. {  
  3.     LoginResult = "";  
  4.     RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();  
  5.     if (string.Compare(Request.RequestType, "get"true)==0)  
  6.     {  
  7.         //将私钥存Session中  
  8.         Session["private_key"] = rsa.ToXmlString(true);  
  9.     }  
  10.     else  
  11.     {  
  12.         bool bLoginSucceed = false;  
  13.         try  
  14.         {  
  15.             string strUserName = Request.Form["txtUserName"];  
  16.             postbackUserName = strUserName;  
  17.             string strPwdToDecrypt = Request.Form["encrypted_pwd"];  
  18.             rsa.FromXmlString((string)Session["private_key"]);  
  19.             byte[] result = rsa.Decrypt(HexStringToBytes(strPwdToDecrypt), false);  
  20.             System.Text.ASCIIEncoding enc = new ASCIIEncoding();  
  21.             string strPwdMD5 = enc.GetString(result);  
  22.             if (string.Compare(strUserName, "user1"true)==0 && string.Compare(strPwdMD5, "14e1b600b1fd579f47433b88e8d85291"true)==0)  
  23.                 bLoginSucceed = true;  
  24.         }  
  25.         catch (Exception)  
  26.         {  
  27.   
  28.         }  
  29.         if (bLoginSucceed)  
  30.             LoginResult = "登录成功";  
  31.         else  
  32.             LoginResult = "登录失败";  
  33.     }  
  34.   
  35.     //把公钥适当转换,准备发往客户端  
  36.     RSAParameters parameter = rsa.ExportParameters(true);  
  37.     strPublicKeyExponent = BytesToHexString(parameter.Exponent);  
  38.     strPublicKeyModulus = BytesToHexString(parameter.Modulus);  
  39. }  


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值