PHP开发日志 ━━ jsrsasign、jsencrypt、php实现前后端数据的RSA加密和解密

按照等保要求,密码类的信息必须经过RSA加密后才能传递:

需要对密码传输过程中进行加密方式传输。
如采用md5加验或SM1、SM2、SM4、3DES、AES、RSA加密算法,
同时也不要直接采用暴露在互联网上的md5、base64算法、URL编码转换。

虽然仅仅用base64来加密已被禁止,但是思路还是一样的:“客户端使用js加密服务端使用php解密”。

比较两个客户端js插件:

jsencrypt.jsjsrsasign.js 都可以实现加密解密,但 jsencrypt.js 更方便, jsrsasign.js 更全面;
jsencrypt.js 加密后就已经是 base64格式 ,而 jsrsasign 需要分两步分别实现 RSA加密base64编码

好了,话不多说,言归正传。

一、原理

RSA基本原理就是客户端上有一把公钥,服务器上有一把私钥,这两把钥匙长相不同,但有着不可思议的数学关系,能互相识别对方。

客户端传递信息前,先利用公钥把信息加密(加密过程有随机数参与,所以每次结果都不同),到达后,服务端利用私钥再把信息解密还原成原信息。

正是由于公私两把钥不一样,甚至其中一把是外人所不知的,所以这种加密方式也叫“非对称加密”。

实际使用时把公钥对外公布,可以由很多客户端拥有,而私钥只限服务端,这样由于没有私钥,即便客户端传递数据时被人拦截,那这些被拦截的数据也是无法解密的。

为了进一步提高安全性,还可以加入签名,这里就不赘述了。

参考学习
《RSA加密、解密、签名、验签的原理及方法》
《RSA算法》


二、配置

配置并获得公钥和私钥。

  • 下载加密插件
    二选一即可。
  1. jsencrypt.js
    地址:https://github.com/travist/jsencrypt,解压后将其中jsencrypt.min.js安装到指定目录。

  2. jsrsasign.js
    地址:https://kjur.github.io/jsrsasign/,解压后将其中jsrsasign-all-min.js安装到指定目录。

  • 配置服务器php.ini
    去掉 ; ,激活extension=openssl

  • 获取公私密钥
    二选一即可

  1. 文件生成的方法:《RSA公钥、私钥生成,详细讲解》;
  2. 在线生成的链接:在线RSA加密/解密工具
    PHP开发日志 ━━ jsrsasign、jsencrypt、php实现前后端数据的RSA加密和解密_第1张图片

本文代码案例以第二种“在线生成”后的公私钥字符串作为变量进行操作。

//注意每隔64位必须要有一个换行
//公钥
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC2Kh46ZE2/k6hBfRHXOm37lQh
Q9g7phdw2jGo0KNgW+wxW2pmZzQhLg+Dx9FyEXa3j0YraktnCIBkx816DLw/xUIh
RObjYX4+5hUnv6IGtHf5pNyP9v/utiRdkko+Vj1BDWtI4lw/0DXruLJIV06EIf95
TFfZaEk81/EO31FdpQIDAQAB
-----END PUBLIC KEY-----

//私钥
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCC2Kh46ZE2/k6hBfRHXOm37lQhQ9g7phdw2jGo0KNgW+wxW2pm
ZzQhLg+Dx9FyEXa3j0YraktnCIBkx816DLw/xUIhRObjYX4+5hUnv6IGtHf5pNyP
9v/utiRdkko+Vj1BDWtI4lw/0DXruLJIV06EIf95TFfZaEk81/EO31FdpQIDAQAB
AoGATyoMTBNsY2xbYDr8/4wsb7cHOZUVp4km14V71BpfLnaPIZGgf1JjjMuUYXOs
uantPMO3fZ7y/eR74f2syPIZllFqSI0CP1HkDyTH5BsLqKqTmzWcbvrWx1Pc2Dis
OCf9KVz4o/Scly3igI8Nblrk5zP03NMBb7dqyNzqsP2i6gUCQQDGojaHEWZ4qTZm
wFhTccKX/DHc1vJesjS3JQHRU6TDWSML476KGoH3+S23XRfRsIFzN1QWrhqfopfg
phP2gcPnAkEAqKKnmAOY0BhzMh0D7q94dtGZbg/MxQPpBRWvEcLsragv8NilM5Mp
6gUjLcWSgEoBq4VcywQE/AOosXbOaQcgkwJAdedRgIkGjza50PH5O8a54CdVnaWF
Bkq3WcLAunTwxvfBAsyzjBxB62RgC4hZnCEuJarA4hmEOh90EWfjT54lHwJALSni
2MOd2Z2yvGko9HPqP2hDP0bcAKfbcJEuIgOif5/btxVOqVFwmExn74pKgjFP4TAG
ehjJfPU96Ml43ogaIQJAYpbORQgUvJilLJGTIisWNF8Wa+lEt1J4tn7p47IDMOAa
CwcbmDGnhnh67CXrwwJvb4F8tK/NiwNqlLhyDrONyg==
-----END RSA PRIVATE KEY-----

三、代码

  1. 演示案例目录如图:
    PHP开发日志 ━━ jsrsasign、jsencrypt、php实现前后端数据的RSA加密和解密_第2张图片

  2. 客户端:index.html

<script type="text/javascript" src="jQuery.min.js"></script>
<script type="text/javascript" src="jsencrypt.min.js"></script>
<script type="text/javascript" src="jsrsasign-all-min.js"></script>
<script type="text/javascript">
//公钥,注意换行符\n
var pubKey = "-----BEGIN PUBLIC KEY-----\n" +
             "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC2Kh46ZE2/k6hBfRHXOm37lQh\n" +
             "Q9g7phdw2jGo0KNgW+wxW2pmZzQhLg+Dx9FyEXa3j0YraktnCIBkx816DLw/xUIh\n" +
             "RObjYX4+5hUnv6IGtHf5pNyP9v/utiRdkko+Vj1BDWtI4lw/0DXruLJIV06EIf95\n" +
             "TFfZaEk81/EO31FdpQIDAQAB\n" +
             "-----END PUBLIC KEY-----";

//用户录入的内容
var content = 'hello world';
console.log('content:' + content);

//=======方法一:jsencrypt初始化,创建加密对象实例=======
var encryptor = new JSEncrypt();  
//设置公钥
encryptor.setPublicKey(pubKey);
//加密,已经实现了RSA+base64
var c = encryptor.encrypt(content);
console.log('jsencrypt Base64:' + c);
//jsencrypt方式提交
jQuery.post("check.php",{"c":c},function(r){
	console.log('jsencrypt Result:' + r);
});

//=======方法二:jsrsasign初始化=======
var pub = KEYUTIL.getKey(pubKey);
//RSA加密,生成16进制字符串
var enc = KJUR.crypto.Cipher.encrypt(content,pub);
console.log('jsrsasign RSA:' + enc);
//base64对数据重新编码
c = hextob64(enc);
console.log('jsrsasign Base64:' + c);
//jsrsasign方式提交
jQuery.post("check.php",{"c":c},function(r){
	console.log('jsrsasign Result:' + r);
});
</script>

PHP开发日志 ━━ jsrsasign、jsencrypt、php实现前后端数据的RSA加密和解密_第3张图片

参考学习
《前端利用jsencrypt.js进行RSA加密》
《JS 使用RSA加密解密》
《采用JSEncrypt,js与PHP服务端加密》
《jsrsasign使用笔记(加密,解密,签名,验签)》
《前端 rsa加密参数的实现(加密库:jsrsasign)》

  1. 服务端:check.php
 
//注意每隔64位必须要有一个换行
//公钥,本变量在本例中并未用到
/*$public_key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC2Kh46ZE2/k6hBfRHXOm37lQh
Q9g7phdw2jGo0KNgW+wxW2pmZzQhLg+Dx9FyEXa3j0YraktnCIBkx816DLw/xUIh
RObjYX4+5hUnv6IGtHf5pNyP9v/utiRdkko+Vj1BDWtI4lw/0DXruLJIV06EIf95
TFfZaEk81/EO31FdpQIDAQAB
-----END PUBLIC KEY-----';*/
//私钥
$private_key = '-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCC2Kh46ZE2/k6hBfRHXOm37lQhQ9g7phdw2jGo0KNgW+wxW2pm
ZzQhLg+Dx9FyEXa3j0YraktnCIBkx816DLw/xUIhRObjYX4+5hUnv6IGtHf5pNyP
9v/utiRdkko+Vj1BDWtI4lw/0DXruLJIV06EIf95TFfZaEk81/EO31FdpQIDAQAB
AoGATyoMTBNsY2xbYDr8/4wsb7cHOZUVp4km14V71BpfLnaPIZGgf1JjjMuUYXOs
uantPMO3fZ7y/eR74f2syPIZllFqSI0CP1HkDyTH5BsLqKqTmzWcbvrWx1Pc2Dis
OCf9KVz4o/Scly3igI8Nblrk5zP03NMBb7dqyNzqsP2i6gUCQQDGojaHEWZ4qTZm
wFhTccKX/DHc1vJesjS3JQHRU6TDWSML476KGoH3+S23XRfRsIFzN1QWrhqfopfg
phP2gcPnAkEAqKKnmAOY0BhzMh0D7q94dtGZbg/MxQPpBRWvEcLsragv8NilM5Mp
6gUjLcWSgEoBq4VcywQE/AOosXbOaQcgkwJAdedRgIkGjza50PH5O8a54CdVnaWF
Bkq3WcLAunTwxvfBAsyzjBxB62RgC4hZnCEuJarA4hmEOh90EWfjT54lHwJALSni
2MOd2Z2yvGko9HPqP2hDP0bcAKfbcJEuIgOif5/btxVOqVFwmExn74pKgjFP4TAG
ehjJfPU96Ml43ogaIQJAYpbORQgUvJilLJGTIisWNF8Wa+lEt1J4tn7p47IDMOAa
CwcbmDGnhnh67CXrwwJvb4F8tK/NiwNqlLhyDrONyg==
-----END RSA PRIVATE KEY-----';

//可以先预判一下openssl扩展是否开启
//if (extension_loaded('openssl')){}

//私钥定位,如果是文件,则使用$private_key_res=file_get_contents($path)
$private_key_res = openssl_pkey_get_private($private_key);
if(!$private_key_res ){
	//私钥不可用
	return FALSE;
}
//返回值
$data = '';
//解码,可能出现加号被转成空格的情况
$password = base64_decode(str_replace(' ','+', $_POST['c']));
//解密
$result = openssl_private_decrypt($password, $data, $private_key_res);
if(!$result ){
	//解密失败
	return FALSE;
}
echo  $data;
?>

参考学习
《php RSA加解密》
《PHP RSA加解密详解(附代码)》

  1. 结果
    在这里插入图片描述

demo版本点击此处:《RSA:js加密,php解密》


四、注意

  • 实际开发时可以把公私钥分别作为变量存入服务端脚本,把公钥传给客户端页面上的js,这样便于未来随时更改密钥;
  • 由于js字符串变量不能在一对引号里换行,所以需要利用 \n+
//例如
var pubKey = "-----BEGIN PUBLIC KEY-----\n" +
             "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC2Kh46ZE2/k6hBfRHXOm37lQh\n";
  • 也可以把公钥变量存入一个表单控件,这样 js 获得的 value 就不用手动增加 \n 来改写公钥了;
//例如
<input type="hidden" id="pk" value="$public_key;?>" />
<script type="text/javascript">
var v = jQuery('#pk').val();
console.log(v);
</script>
  • 总之其主要目的就是不能缺少换行,方法多来西;
  • 有文章说可能在传递时会将 + 变成 空格,所以要完成一次过滤。当然,直到目前我还没发现有这个问题;
str_replace(' ','+', $p)
  • 还有一些注意点或问题请点击浏览:
    《前端使用RSA(jsencrypt.js)加密数据传递到后端解密失败》
    《关于jsencrypt.js 这个RSA库 签名模式客户端解密后返回false》
    《PHP RSA无法解密出来 解密为空 解决方法》
    《RSA 加密 明文长度 超出》
    《PHP如何判断某项扩展是否开启》

你可能感兴趣的:(安全,PHP,前端,javascript,php,安全,rsa,加密)