最近在做react native项目,需要对用户登录数据进行加密传输,前端加密采用开源js库crypto-js,后端解密用php(我的是php7版本)自带的openssl_decrypt方法,踩了许多坑,记录一下。
一、安装/配置依赖库
1. crypto-js安装
npm install crypto-jsGitHub项目地址:https://github.com/brix/crypto-js
官方文档中有介绍具体使用的方法,很清晰。
2. php openssl 配置
参考文章:https://www.cnblogs.com/phpxuetang/p/5656266.html
openssl_encrypt的基本使用形式:openssl_decrypt(data, method, key, options, iv)
data:待解密的数据
method:可以选aes-128-cbc、aes-256-cbc等,官网上的method列表如下:
(
[0] => AES-128-CBC
[1] => AES-128-CFB
[2] => AES-128-CFB1
[3] => AES-128-CFB8
[5] => AES-128-OFB
[6] => AES-192-CBC
[7] => AES-192-CFB
[8] => AES-192-CFB1
[9] => AES-192-CFB8
[11] => AES-192-OFB
[12] => AES-256-CBC
[13] => AES-256-CFB
[14] => AES-256-CFB1
[15] => AES-256-CFB8
[17] => AES-256-OFB
[18] => BF-CBC
[19] => BF-CFB
[21] => BF-OFB
[22] => CAST5-CBC
[23] => CAST5-CFB
[25] => CAST5-OFB
[41] => IDEA-CBC
[42] => IDEA-CFB
[44] => IDEA-OFB
[53] => aes-128-cbc
[54] => aes-128-cfb
[55] => aes-128-cfb1
[56] => aes-128-cfb8
[58] => aes-128-ofb
[59] => aes-192-cbc
[60] => aes-192-cfb
[61] => aes-192-cfb1
[62] => aes-192-cfb8
[64] => aes-192-ofb
[65] => aes-256-cbc
[66] => aes-256-cfb
[67] => aes-256-cfb1
[68] => aes-256-cfb8
[70] => aes-256-ofb
[71] => bf-cbc
[72] => bf-cfb
[74] => bf-ofb
[75] => cast5-cbc
[76] => cast5-cfb
[78] => cast5-ofb
[94] => idea-cbc
[95] => idea-cfb
[97] => idea-ofb
)
options:有OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING两种,前者会默认采用PKCS#7进行补位,输出结果是加密后的原始结果,没有用base64编码;后者要求被加密的数据必须是“加密块”的整数倍,也就需要自己对加密数据进行补位处理。
iv:一个初始非零向量(必须是16位)
二、采坑记录
我采用的是aes加密的方式,
注意坑1:openssl_encrypt中aes-128-cbc、aes-256-cbc中的128、256是与秘钥位数有关的,16位秘钥需要使用aes-128-cbc模式。参考文章:https://www.douban.com/note/628737539/
注意坑2:加密后的字符串如果直接用post form形式提交给php后端,会出现无法解密的情况,经过多次测试,终于找到原因。
这是我form表单里提交的密文:
php后端获取'data'数据后得到的密文:
详细看!字符'+'还有'/'解析方式不同,得到的是完全不同的密文!!
所以,我最后前端是用json格式传输的数据,php后端用file_get_contents("php://input");获取前端传送过来的json数据。php获取json数据可参考文章:http://blog.csdn.net/lee00047/article/details/73650433
三、代码实现//前端js,使用crypto-js对数据进行AES加密
function encrypt(text){
var key = CryptoJS.enc.Latin1.parse('1234567890654321'); //为了避免补位,直接用16位的秘钥
var iv = CryptoJS.enc.Latin1.parse('1234567890123456'); //16位初始向量
var encrypted = CryptoJS.AES.encrypt(text, key, {
iv: iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.ZeroPadding
});
return encrypted;
}
//后端php,类函数appLogin()
public function appLogin(){
$post = $this->request->post();
$encrypted = $post['data'];
$key = "1234567890654321";
$iv = "1234567890123456";
$decrypted = openssl_decrypt($encrypted, 'aes-128-cbc', $key, OPENSSL_ZERO_PADDING , $iv);
…… //其他处理,解密后的字符串是带有'u0000'补位的原始结果,需要自行去掉
}