【小白学加密】RSA+AES混合加密长数据(vue前端+java后端)
目录
项目背景:
前端vue
后端springboot
问题:
分析:
今日,在进行毕业设计编写过程中发现在vue中使用jsencrypt包进行rsa加密一个2048字节的敏感数据(一份私钥)时后端接收到的加密后信息为false。在前端调式后发现控制台报错:Message too long for RSA,明显需要加密的数据长度大于rsa加密的长度上限。
解决方案:
对于此种情况,有两种解决方式:
1.将需要加密的数据拆分为多段,分段加密后拼接返回后端。后端在分段解密。
2.使用对称密钥加密(aes),在将对称密钥非对称加密(rsa),将加密后的密钥和数据返回后端,后端先解密对称密钥,再解密数据。
相较于原有的rsa加密比起来,个人认为后者改造更为简单。
解决实现:
前端:
准备工作:
安装并引入加解密依赖
//Rsa
npm install jsencrypt
//Aes
npm install jsencrypt
import { JSEncrypt } from 'jsencrypt'
import CryptoJS from 'crypto-js';
过程:
rsa加密使用的公钥由后端提供
aes加密使用的公钥由前端随机生成,并传递给后端解密
步骤:
1.前端请求并获取rsa密钥
2.前端随机生成aes密钥和加盐字段(iv)
3,前端使用aes密钥和iv加密长数据
4.前端使用rsa公钥加密aes密钥和iv
5.前端将加密后的长数据,加密后的aes密钥和iv传递给后端
6.后端使用rsa私钥解密aes密钥和iv
7.后端使用aes密钥和iv解密长数据
代码实现:
前端请求并获取rsa密钥:
http请求获取,略过
封装随机生成aes密钥方法:
export const InitAESKey = () => {
const key = CryptoJS.lib.WordArray.random(8)
const keyHex = key.toString(CryptoJS.enc.Hex)
return keyHex
}
封装aes加密方法:
这里为了省事所以让iv和密钥保持一致
export const AESEncrypt = (aesKey,data) => {
const realyKey = CryptoJS.enc.Utf8.parse(aesKey)
const encryptedData = CryptoJS.AES.encrypt(data, realyKey,{
//这里偷懒了让iv和密钥保持一致
iv: CryptoJS.enc.Utf8.parse(aesKey),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString()
return encryptedData
}
封装Rsa加密方法:
rsa加密简单实现
export const RsaEncrypt = (pubKey,str) => {
let encryptor = new JSEncrypt()
// 设置公钥
encryptor.setPublicKey(pubKey)
return encryptor.encrypt(str)
}
前端执行代码:
//请求后端获取rsa公钥
const rsa_res = await getRsaPubKeyAPI();
const pubKey = rsa.data;
//随机生成aes密钥(和iv)
const aesKey = InitAESKey()
//aes加密 addEcoForm.value.longData是需要加密的长数据
const aesLongData = AESEncrypt(aesKey, addEcoForm.value.longData);
//交给后端执行
const res = await bindForUser({
aesData: aesLongData,
rsaAesKey: RsaEncrypt(pubKey, aesKey),
});
//成功提示
if (res.success) {
ElMessage({
type: "success",
message: "操作成功",
});
}
后端:
准备工作:
引入依赖,这里推荐hutool工具包
<!-- AES -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.68</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.24</version>
</dependency>
代码实现:
封装前端的请求参数:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Request implements Serializable {
@Serial
private static final long serialVersionUID = 6534982605469082958L;
//aes加密的长数据
private String aesData;
//rsa加密的aes密钥
private String rsaAesKey;
}
rsa工具:
下面是使用hutool工具包随机生成了rsa的公钥和私钥,并声明为bean。这样rsa对象将在项目启动时初始化,并在本次允许过程中一直存在,同时交给ioc容器管理,在任意需要的地方使用
@Configuration
public class RsaConfig {
private final String KEY_TYPE = "RSA";
@Bean
public RSA rsa(){
KeyPair keyPair = SecureUtil.generateKeyPair(KEY_TYPE);
PublicKey pubKey = keyPair.getPublic();
PrivateKey priKey = keyPair.getPrivate();
return new RSA(priKey,pubKey);
}
}
解密方法:
aes对象的构造参数:"CBC", "PKCS7Padding"分别对应了前端封装aes加密方法的:
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
@Autowired
//注入rsa对象
private RSA rsa;
public viod decrypt(Request request) {
//校验参数合法性,不重要
checkBindRequest(request);
//rsa私钥解密出aes密钥
String aesKey = rsa.decryptStr(request.getRsaAesKey(), KeyType.PrivateKey);
//构造aes对象
AES aes = new AES("CBC", "PKCS7Padding", aesKey.getBytes(), aesKey.getBytes());
//解密出长数据
String data = new String(aes.decrypt(request.getAesData()));
}
总结:
产生问题的原因是js的jsencrypt提供的rsa非对称加密无法加密超过117bytes的数据,但对称加密aes就没有如此严苛的限制,所以才要使用aes对称加密代替rsa加密长数据。同时又为了确保aes密钥的安全,使用rsa加密aes密钥。
更多推荐
所有评论(0)