为什么Vue项目中混合加密发送的密文到后端解密失败?

西门歆艺 阅读 72

最近在做一个需要混合加密的登录功能,用RSA加密对称密钥然后AES加密密码,但后端总说解密失败。

代码逻辑是这样的:先用后端给的公钥加密AES的密钥,再用这个密钥加密密码,然后一起发过去。但测试时后端说RSA解密密钥失败。

这是我的Vue组件代码片段:



import { JSEncrypt } from 'jsencrypt';
import { AES, enc } from 'crypto-js';

export default {
  methods: {
    async submit() {
      const password = '123456';
      const aesKey = Crypto.randomBytes(16).toString('hex'); // 这里生成的AES密钥
      const encryptor = new JSEncrypt();
      encryptor.setPublicKey('MII...');
      
      // 这里先加密AES密钥
      const encryptedKey = encryptor.encrypt(aesKey);
      
      // 再加密密码
      const encryptedPassword = AES.encrypt(password, aesKey).toString();
      
      // 发送到后端
      await axios.post('/login', { key: encryptedKey, password: encryptedPassword });
    }
  }
}

我检查过公钥格式没问题,也确认过Base64编码,但后端说RSA解密时出现illegal key错误。难道是密钥生成的方式不对吗?或者AES加密时的密钥格式有问题?

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
夏侯世鹏
问题应该出在你生成 AES 密钥的方式不对。

你用的是 Crypto.randomBytes(16).toString('hex'),这个生成出来的是 32 字节的 hex 字符串(每个字符是 4 位),但 AES 要求的密钥字节数必须是 16 字节(AES-128)、24 字节(AES-192)或 32 字节(AES-256)。

你这样生成出来的密钥长度是对的(如果是 32 字节就是 AES-256),但问题在于你传给 RSA 加密的这个字符串是 hex 字符串(只包含 0-9 和 a-f),而后端在用 RSA 解密时很可能期望的是原始字节流,不是 hex 字符串,这就导致解密失败。

你需要把 AES 密钥以 Buffer 形式生成,并且加密前要转成 Base64 或 Binary 字符串,而不是 Hex。

正确的做法应该是这样:

const aesKeyBuffer = Crypto.randomBytes(32); // 生成 32 字节的 Buffer
const aesKeyB64 = aesKeyBuffer.toString('base64'); // 转成 Base64 字符串

encryptor.setPublicKey('MII...');
const encryptedKey = encryptor.encrypt(aesKeyB64); // 加密 Base64 字符串


这样后端拿到的是 Base64 字符串,解密后就可以还原成原始的 AES 密钥 Buffer。

另外还有一个潜在问题:AES 加密时不能直接用字符串当密钥,crypto-js 内部会自动做处理,但为了保险起见建议改成这样:

const encryptedPassword = AES.encrypt(
password,
enc.Base64.parse(aesKeyB64)
).toString();


这样保证你传给 AES 的密钥是 WordArray 格式,不是字符串。
点赞 7
2026-02-04 15:06
司徒东辰
问题出在你的AES密钥生成和RSA加密的格式不一致。后端拿到的密文解不出来,大概率是前端传过去的密钥格式不对。

先说重点:你用 Crypto.randomBytes(16).toString('hex') 生成的AES密钥是个32字符的十六进制字符串,但RSA加密时需要的是原始字节数组(Buffer)。后端解密出来的结果格式跟你预期的不一样,自然就报错了。

解决办法很简单,把AES密钥生成改成这样:

const aesKey = Crypto.randomBytes(16); // 不要转成hex字符串


然后RSA加密时也要注意,JSEncrypt 默认会把输入当成字符串处理,所以你需要先把AES密钥转成Base64编码再加密:

const encryptedKey = encryptor.encrypt(aesKey.toString('base64'));


最后,AES加密密码时直接用这个Buffer格式的密钥就行,AES-CryptoJS 能自动处理。

完整修改后的代码大概是这样的:

const aesKey = Crypto.randomBytes(16); // 生成16字节的Buffer
const encryptor = new JSEncrypt();
encryptor.setPublicKey('MII...');
const encryptedKey = encryptor.encrypt(aesKey.toString('base64')); // 先转Base64再加密

const encryptedPassword = AES.encrypt(password, aesKey).toString(); // 这里直接用Buffer格式的密钥

await axios.post('/login', { key: encryptedKey, password: encryptedPassword });


记得提醒后端,解密出来的AES密钥是Base64编码的,得转回Buffer才能用。这种前后端加密通信真的挺磨人的,多测试几次吧,缓存起来这些细节以后还能用。
点赞 12
2026-01-30 05:01