AES 与 RSA 混合加密
最近工作需要又在折腾 PHP,碰到用户信息上传的需求,自然是不能明文上的。 所以整了一个加密方案,大概像这样。
前端直接用简单的对称加密方式肯定是在骗自己,再怎么混淆别人有心也是能破解的。 但都用 RSA 的话,那真是不知道要用多少位的 key 来做了,而且运算量也太大了, 多少会影响性能。
总体来说就是利用 RSA 的不对称性来确保前端加密的安全,用 AES 来进行主要的数据编码, 保证运行的效率。
前端
实现的时候 AES 选用的加密库是 crypto-js, 支持许多种加密方式,可以安旭选择。
不过选库的时候踩了一个坑,github 上搜 crypto js 的时候,第一名那个项目其实是把 crypto-js 包了一层,简化了一下 API。单纯在 js 里使用的时候没什么问题,但后面在 PHP 那边解码的时候, 总是会出现乱码,到现在都没找到原因,后来换成上面那个链接的包才可以。
首先引用压缩包中两个文件
<script src="/rollups/aes.js"></script>
<script src="/components/pad-nopadding-min.js"></script>
其实第二个文件只是一小段位数补齐代码而已,直接合起来好一些。接下来就可以进行 AES 加密了
function createRandomStr(lenght) {
var str = '';
while (lenght-- > 0) {
var num = Math.floor(Math.random() * 26);
str += String.fromCharCode(num + 97);
}
console.log('random string');
console.log(str);
return str;
}
function aesEncrypt(dataStr) {
var randomStr = createRandomStr(16);
aesKey = CryptoJS.enc.Latin1.parse(randomStr);
var result = CryptoJS.AES.encrypt(dataStr, aesKey, {
iv: aesKey,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
return {
// 加密猴得到的 result 是一个对象,须要调用 toString 再交给后端
data: result.toString(),
key: randomStr
};
}
RSA 加密使用的是 JSEncrypt,同样是先引用
<script src="/jsencrypt.min.js"></script>
加密过程也很简单
var encrypt = new JSEncrypt();
var publicKey = '-----BEGIN PUBLIC KEY----- ...';
function rsaEncrypt() {
encrypt.setPublicKey(publicKey);
return encrypt.encrypt();
}
接下来就是把两个加密方式连起来。
funciton encryptData(data) {
var dataStr = JSON.stringify(data);
var aesResult = aesEncrypt(dataStr);
return {
key: rsaEncrypt(aesResult.key),
data: aesResult.data
};
}
然后把数据提交到后端接口就可以了
后端
后端使用的是 PHP,须要安装一个扩展 mcrypt,具体可以看一下文章末尾的参考链接。 还需要 openssl 的支持,剩下的就很简单了
写个 RSA 加解密的类
class Rsa_tool {
private static $PRIVATE_KEY = 'xxxxxx';
private static $PUBLIC_KEY = 'xxxxxx';
public static function rsaEncrypt($originalData) {
$pu_key = openssl_pkey_get_public(self::$PUBLIC_KEY);
$encrypted = '';
openssl_public_encrypt($originalData, $encrypted, $pu_key);
$encrypted = base64_encode($encrypted);
return $encrypted;
}
public static function rsaDecrypt($encryptedData) {
$pi_key = openssl_pkey_get_private(self::$PRIVATE_KEY);
$decrypted = '';
openssl_private_decrypt(base64_decode($encryptedData), $decrypted, $pi_key);
return $decrypted;
}
}
再加上 AES 解密,就完成了
$key = $_POST['key'];
$data = $_POST['data'];
$key = Ras_tool::rsaDecrypt($key);
$data = base64_decode($data);
// 注意模式须要和加密的时候一样
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $key);
// AES 解密后有可能在字符串末尾留有 \0 补齐尾,不去除的话有可能会 json 解析失败,所以用 trim 过一遍
$data = trim($data);
var_dump($data);