目录

项目背景:

问题:

分析:

解决方案:

解决实现:

前端:

准备工作:

代码实现:

前端请求并获取rsa密钥:

封装随机生成aes密钥方法: 

封装aes加密方法:

封装Rsa加密方法:

前端执行代码:

后端:

准备工作:

代码实现:

封装前端的请求参数:

rsa工具:

解密方法:

总结:


项目背景:

前端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密钥。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐