最近有一个需求,需要前端把传参整体加密,然后由后端解密,对比传参数据,防止数据篡改.
使用 RSA 非对称加密,前端公钥加密,后端私钥解密
遇到的问题
之前前端都是用 jsencrypt.js 进行加密,但是有一个问题,
这个加密方法对数据长度有限制,超过100个字符加密就会报错,而我们传参的参数不可能都控制在100个字符内.
之后发现了另外一个js工具 encryptjs,可以加密超长的数据
encryptjs 的使用
encryptjs 文档地址
npm install encryptjs --save-dev
var encrypt=require('encryptjs');
调用 encryptLong() 方法加密
后端处理
后台调用正常解密方法发现解密失败了,因为encryptLong加密实际上是分段加密,后端同样也要分段解密
后端分段解密代码
public String privateKeyDecrypt(String content)
throws Exception {
//调用Java加密的Cipher对象,
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,privateKey);//第一个参数表示这是加密模式,第二个参数表示私钥密钥
byte[] bytesContent = Base64.getDecoder().decode(content.getBytes());
int inputLen = bytesContent.length;
int offLen = 0;
int i = 0;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while(inputLen - offLen > 0){
byte[] cache;
if(inputLen - offLen > 128){
cache = cipher.doFinal(bytesContent,offLen,128);
}else{
cache = cipher.doFinal(bytesContent,offLen,inputLen - offLen);
}
byteArrayOutputStream.write(cache);
i++;
offLen = 128 * i;
}
byteArrayOutputStream.close();
byte[] byteArray = byteArrayOutputStream.toByteArray();
//使用Base64对加密结果进行编码
return new String(byteArray);
}
每次通过128字节循环解密
这个时候又有一个新的问题,发现参数没有中文可以解密成功,有中文就会解密失败
这个问题最后通过在前端加密前先使用 encodeURIComponent() 方法对字符串转码在加密
后端解密后同样转码解决
转码的问题
使用 JDK 自带的 URLDecoder.decode() 方法解密原字符串,有空格的话空格会转成+号
推荐使用 springboot 提供的 UriUtils.decode() 方法不会有这个问题.