用户名密码明文直接POST到后端,很容易被别人从监听到。注:包括使用MD5等哈希函数处理后的数据,这里也算做明文(现在MD5爆破网站已经很多了~)。对安全性要求较高的网站,比如银行和大型企业等都会使用HTTPS对其进行加密通讯。但是由于效率原因,使用HTTPS的代价是及其昂贵的,对于访问量稍大的网站就会造成严重的性能瓶颈。解决方法一般只能采用专门的SSL硬件加速设备如F5的BIGIP等。所以很多网站选择了模拟SSL的做法,使用RSA来对密码等安全信息进行公钥加密,服务端用私钥解密。
通常是对密码进行加密,具体如下:
1.加载三个RSA的js库文件,可以到这里下载 http://www.ohdave.com/rsa/。
2.获取秘钥:
#1. 相关信息:
通常情况下网站的SSL证书是由专门的CA机构(如VeriSign)颁发,同时需要交纳一定数额的费用。可是对于平时开发测试或其他情况下,我们自己也可以充当CA来生成自己颁发的证书。当然与前者相比缺点很明显:不能获得各个浏览器的信任,会弹出警告提示。不过,好消息是,对安全性要求稍低的网站现在可以考虑使用免费的CA认证(貌似是其级别最低的证书)。
跟VeriSign一样,StartSSL(网址:http://www.startssl.com,公司名:StartCom)也是一家CA机构,它的根证书很久之前就被一些具有开源背景的浏览器支持(Firefox浏览器、谷歌Chrome浏览器、苹果Safari浏览器等)。在2009年9月份,StartSSL竟然搞定了微软:微软在升级补丁中,更新了通过Windows根证书认证程序(Windows Root Certificate Program)的厂商清单,并首次将StartCom公司列入了该认证清单,这是微软首次将提供免费数字验证技术的厂商加入根证书认证列表中。现在,在Windows 7或安装了升级补丁的Windows Vista或Windows XP操作系统中,系统会完全信任由StartCom这类免费数字认证机构认证的数字证书,从而使StartSSL也得到了IE浏览器的支持。
#2.要生成获得证书所需的密钥等文件:
openssl genrsa -des3 -out server.pem 1024
openssl req -new -key server.pem -out server.csr
openssl rsa -in server.pem -out server.pem
使用上面的命令就会创建一个证书申请,这里我们会要求输入国家、组织、姓名等信息,但是不会要求输入证书有效天数,因为证书有效天数是CA认证中心给我们的;然后我们会把这个生成好的cert.csr(Certificate Signing Request (CSR):证书签名申请)发给CA认证中心。CA认证中心通过后,会反馈(通常是邮件)回来认证的信息,再导入即可。
把上面生成的文件内容提交给CA,即可换取证书;若自行生成则:
openssl x509 -req -days 365 -in server.csr -signkey server.pem -out server.crt
把它们放到指定目录(例如:/ssl/),供下一步使用。
#3.获取十六进制的密钥:
数据是用ASN.1编码过的,所以可以用openssl命令从密钥文件(key或pem)提取秘钥
openssl asn1parse -out temp.ans -i -inform PEM < server.pem
3.javascript 加密代码:
function rsa_pwd(content){
//十六进制公钥
var rsa_n = "DB89C01D4550F9974C30AF5370214F3....";
setMaxDigits(131); //131 => n的十六进制位数/2+3
var key = new RSAKeyPair("10001", '', rsa_n); //10001 => e的十六进制
content_rsa = encryptedString(key, content); //不支持汉字
return content_rsa;
}
4.php 加密/解密代码:
<?php
/**
* 公钥加密
*
* @param string 明文
* @param string 证书文件(.crt)
* @return string 密文(base64编码)
*/
function publickey_encodeing($sourcestr, $fileName) {
$key_content = file_get_contents($fileName);
$pubkeyid = openssl_get_publickey($key_content);
if (openssl_public_encrypt($sourcestr, $crypttext, $pubkeyid)) {
return base64_encode("".$crypttext);
}
}
/**
* 私钥解密
*
* @param string 密文(二进制格式且base64编码)
* @param string 密钥文件(.pem / .key)
* @param string 密文是否来源于JS的RSA加密
* @return string 明文
*/
function privatekey_decodeing($crypttext, $fileName, $fromjs = FALSE) {
$key_content = file_get_contents($fileName);
$prikeyid = openssl_get_privatekey($key_content);
$crypttext = base64_decode($crypttext);
$padding = $fromjs ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING;
if (openssl_private_decrypt($crypttext, $sourcestr, $prikeyid, $padding)) {
return $fromjs ? rtrim(strrev($sourcestr), "/0") : "".$sourcestr;
}
return '';
}
?>
5.测试代码:
//JS->PHP 测试 $_POST['password']是js加密后的信息
$txt_en = $_POST['password'];
$txt_en = base64_encode(pack("H*", $txt_en));
$file = 'ssl/server.pem';
$txt_de = privatekey_decodeing($txt_en, $file, TRUE);
var_dump($txt_de);
//PHP->PHP 测试
$data = "汉字:1a2b3c";
$config = Core::getInstance()->config;
$file1 = 'ssl/server.crt';
$file2 = 'ssl/server.pem';
$a = publickey_encodeing($data, $file1);
$b = privatekey_decodeing($a, $file2);
var_dump($b);
6.需注意:
#1.PHP中openssl扩展公私钥加密函数只支持小数据,加密时117字节,解密时128字节。若不然得自己循环加密后合并。
#2.SSL本身也只是用RSA来进行密钥加密,数据加密则是利用这个加密的密钥进行对称加密,以保证速度。所以万不可将其用于大数据量加密!
7.本方案几处优点:
#1.安全性高。基于非对称的RSA算法加密数据,只要在私钥不被暴露的前提下,密钥长度足够长,短时间内基本是无法破解的。
#2.使用方便。前端使用现成的JS库来实现加密,PHP端则可直接使用现成的openssl扩展,而不用RSA的PHP源码实现或自己开发扩展。
#3.速度靠谱。由于RSA解密算法相当复杂,而该操作交由PHP端扩展来实现,效率上比网上的PHP代码要高许多。
#4.便于升级。密钥是直接从linux下openssl工具生成的证书中获取,不仅不用其他密钥生成工具,也方便今后升级到真正的HTTPS。
8.参考文章:
http://www.jishuer.com/static/article-270.html
http://blog.csdn.net/linvo/article/details/5619807
http://blog.sina.com.cn/s/blog_4fcd1ea30100yh4s.html
http://blog.csdn.net/fenglibing/article/details/8610280