RSA非对称算法实现HTTP密码加密传输
目前一般帐号系统,都是https来传输账户性息,申请一个https证书也不贵。但是网站的其它功能并不需要走https协议,https和http混布比较麻烦,所以决定先实现一个http协议传输RSA非对称密钥算法加密密码的方案。这样做只能说是保证不明文传密码,但是并不能防身份伪造,所以其实还是不安全的,只是目前产品能接受,算是一个过渡期吧。有需要的话还是要改成https的。
关于rsa算法,具体参考维基百科相关的介绍。简单来说,用rsa算法产生一对公钥和私钥,通信双方A和B,A用公钥加密要发送的数据,B用私钥来解密A发过来的密文,从而获得a想要发过来的数据。保证rsa密文不容易被破解的理论依据是对极大整数做因数分解的难度非常大。
下面具体看看实现过程。这里前端用到rsa的js实现方案jsencrpt.js ,后端用php的open_pkey_new等相关方法。
1.【后端】先看php生成公钥密钥对相关代码:
function generatePubPri() { $config = array("config" => '/path/to/openssl.cnf'); $res = openssl_pkey_new($config); openssl_pkey_export($res,$pri, null, $config); $d= openssl_pkey_get_details($res); $pub = $d['key']; $pubFd = fopen("pub.txt", "w"); fwrite($pubFd, $pub); fclose($pubFd); $priFd = fopen("pri.txt", "w"); fwrite($priFd, $pri); fclose($priFd); }
这里前提是要安装openssl,php要加载openssl扩展。执行generatePubPri即可的到如下的公钥密钥对,每次执行生成的都不一样。
[xxx@xxx makedemo]$ cat pri.txt -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQCq/8HruBYhems80BluLiiP0uUTzM/NJSFktzxA1rfzjhEg8z0W r0WAvIdbye2vTG0CYi6PGpjxgUkEVaaHLWEIMiA4g3TIFSUp5pmlWRpGNqilrxd4 sXM5wzL13WkN1j6gBfZNJt62mO35A2Ubl9fbNw/YU2KNPR0+EHP0Z6agmwIDAQAB AoGAViPcllf3ngnDN7FE/kH2YO1GRMEp9Re9SLUdfzQrGl/4tPaTUXgdtQpDzbNd Lq97QnYfKiul3BLaq3pSF0p+1AUHtJby/HT1Tqz0380x9Y+QKjJErePycTs28zIq FXmCMnOqxhaWPB89hxCIG0g7bVt9qGRDUZGY05kMwSM9gvECQQDcWjQoBfb4IQls RUQlprzizQD+S4cHJdxCq5qh7TqyH1IPoHq04tQsuYFVEH2+Z0Spimz/oluNuDnJ NppKdJZdAkEAxqmbtzs9bPbyihb9qpsD8Sne1DIo5uRBJ7G4/RE4vazaAXX9HUIS HZg/To5XSGHzgQteIUXJRjpX5sYLCS8zVwJAQxElbMUb/Tu47X5LlpYgSXuSANQm HfPVDWnDn+NfiRVlWaJDlsivQYmYprZlP02ZJW0fbdMRwJnA5NA8t8qydQJBAKZH dERLW0CG+b7HO46+rPAAAbhOO5n2VuqogJOhBIMN2HL8lN0WXh9TPTm9PiUhhzTt lN34kV0snEJWZQpM7YUCQQCgKhoAlp4DpcYvqq569UI7IE4ZL4l9RlkiNG1UYyda iZoNVN7ji8K2ZvOKykJBwDeKIn4JrknUHrjZXEweRKEl -----END RSA PRIVATE KEY----- [xxx@xxx makedemo]$ cat pub.txt -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq/8HruBYhems80BluLiiP0uUT zM/NJSFktzxA1rfzjhEg8z0Wr0WAvIdbye2vTG0CYi6PGpjxgUkEVaaHLWEIMiA4 g3TIFSUp5pmlWRpGNqilrxd4sXM5wzL13WkN1j6gBfZNJt62mO35A2Ubl9fbNw/Y U2KNPR0+EHP0Z6agmwIDAQAB -----END PUBLIC KEY-----
2.【前端】用公钥加密一下密码"123456"。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JS->jsencrypt.js RSA加密实现</title> <script type="text/javascript" src="jsencrypt.js"></script> </head> <body> <script type="text/javascript"> var crypt = new JSEncrypt(); //新建rsa对象 var publickey = '\ -----BEGIN PUBLIC KEY-----\ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq/8HruBYhems80BluLiiP0uUT\ zM/NJSFktzxA1rfzjhEg8z0Wr0WAvIdbye2vTG0CYi6PGpjxgUkEVaaHLWEIMiA4\ g3TIFSUp5pmlWRpGNqilrxd4sXM5wzL13WkN1j6gBfZNJt62mO35A2Ubl9fbNw/Y\ U2KNPR0+EHP0Z6agmwIDAQAB\ -----END PUBLIC KEY-----\ '; crypt.setPublicKey(publickey);//添加来自服务端的publickey var result = crypt.encrypt('123456'); //返回值为加密后的结果 console.log(result); </script> </body> </html>
在浏览器里访问后,控制台会打印出如下的加密字符串:
SonHPbJpQBwygjZ5ZMtybfLEylnNCsd3poBsNxSt3QkUSDe+Pf7lf4JJIot9Ybd7mAXiOUgGZR7VctCSJhzzZQWNZp1or6h6dsYoFHE/dbDHIxJGcXMNfv5BqrfAMGqkQVvyrED3NHcrgXokataRJOrsU7yvKpQKW6e3j+zcZD8=
eRA40/RbbbqtuEC10Ee3NVDsnpfgibn8nRuTaPmvXI1XjVFX8pjwtMxiuT9xaBfX8K+LI/6ccgghYyJdxjd8V+DyxBPz6/QzT3f5eoOz9ULD85r0K//BuKvuTiyQ/NajProvPN3ns6UzxECmuFg0UNtrMNkOdFRpaAtueadKJDU=
Tqgagyx5DlDLI/tcxYsnN/3AbUPCX/EFE6yn5SoVMX3R/RQ6od6b4hT10LUctcBZ649RhHkwzxTFzIFfvbRS87OftOhebGXAP9JpN/xt9IsaXOU4wp8ZiyQKIrClnepXtRaSC10WF/ishsejgo3i7APXs7fWJiEMkoqRYwnbyPo=
。。。。。。奇怪的是每次刷新打印的都不一样。估计都可以用的。
3. 【后端】解密前端发送过来的密文
解密的方法:
/** * rsa解密 * @access public * @param 密文 * @return 解密后的字符串 */ function decrypt($data, $privkey) { if (openssl_private_decrypt(base64_decode($data), $decrypted, $privkey)){ $data = $decrypted; }else { $data = ''; } return $data; }
$priveKey = file_get_contents("pri.txt");
$data = "SonHPbJpQBwygjZ5ZMtybfLEylnNCsd3poBsNxSt3QkUSDe+Pf7lf4JJIot9Ybd7mAXiOUgGZR7VctCSJhzzZQWNZp1or6h6dsYoFHE/dbDHIxJGcXMNfv5BqrfAMGqkQVvyrED3NHcrgXokataRJOrsU7yvKpQKW6e3j+zcZD8=";
echo decrypt($data, $priveKey);
echo "\n";
最后是解密出"123456"
[xxx@xxx makedemo]$ php rsa.php
123456
试了一下另外两个密文,非常棒,发现也是可以解出"123456"。
最后给一个完整的demo,流程如下:
代码如下:
1.submitPassword.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JS->jsencrypt.js RSA加解密实现</title> <script type="text/javascript" src="jsencrypt.js"></script> <script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script> </head> <body> <script type="text/javascript"> var PassWord = "123456"; function myEncrypt(pass) { var crypt = new JSEncrypt(); var publickey = ""; crypt.setPublicKey(publickey); return crypt.encrypt(pass); } var promise = $.ajax({ url: "getPublicKey.php", method: "POST", dataType: "json", data: {} }); promise.done(function(data){ if(data.code === 0) { var passWord = myEncrypt(PassWord); var passPromise = $.ajax({ url: "recievePassword.php", method: "POST", dataType: "json", data: {"password": passWord} }); passPromise.done(function(data){ if(data.code === 0) { console.log("submit success."); }else{ console.log("submit failed."); } }); passPromise.fail(function(err){ console.log(err); }); }else { console.log("submit failed."); } }); promise.fail(function(err){ console.log(err); }); </script>
2.getPublicKey.php
function generatePubPri() { $config = array("config" => '/home/users/zhutianpeng/.jumbo/etc/ssl/openssl.cnf'); $res = openssl_pkey_new($config); openssl_pkey_export($res,$pri, null, $config); $d= openssl_pkey_get_details($res); $pub = $d['key']; $pubFd = fopen("pub.txt", "w"); fwrite($pubFd, $pub); fclose($pubFd); $priFd = fopen("pri.txt", "w"); fwrite($priFd, $pri); fclose($priFd); return $pub; } $publicKey = generatePubPri(); $data = array("code" => 0, "msg" => "success", "data" => array("pubkey" => $publicKey)); echo json_encode($data);
3.recievePassword.php
$passWord = $_POST['password']; $ret = array(); if(!empty($passWord)) { $ret['code'] = 0; $ret['msg'] = "success"; }else { $ret['code'] = 1; $ret['msg'] = "not recieved a password"; } echo json_encode($ret);
[完]