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

一般咱们作一个Web应用程序的时候都须要登陆,登陆就要输入用户名和登陆密码,而且,用户名和登陆密码都是明文传输的,这样就有可能在中途被别人拦截,尤为是在网吧等场合。javascript

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

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

因此,我打算本身实现一个密码加密传输方法。jquery

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

我设计的这个登陆密码加密传输方法的原理图以下:算法

 

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

复制代码
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%>用于回显输入的用户名,<%=LoginResult%>用于显示登陆结果,<%=strPublicKeyExponent%>和<%=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