React表单提交时用AES加密数据,后端返回解密失败怎么办?

松静 ☘︎ 阅读 25

我在React表单提交时用crypto-js的AES加密表单数据,但后端始终报解密失败。明明前后端都用了相同的密钥和模式,到底是哪里出问题了?

代码是这样写的,表单提交时把JSON字符串加密后发送:


import CryptoJS from 'crypto-js';

handleSubmit = () => {
  const data = JSON.stringify(this.state.formData);
  const encrypted = CryptoJS.AES.encrypt(
    data,
    'my-secret-key-123',
    { mode: CryptoJS.mode.ECB }
  ).toString();
  
  fetch('/api/submit', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ encryptedData: encrypted })
  });
};

后端Java用相同的ECB模式和PKCS5Padding,但解密时抛出IllegalBlockSize异常。试过改密钥长度到16位,调整编码格式都没用。是不是前端加密时漏了什么配置?或者ECB模式在React里有问题?

我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
Des.玉茂
你这个问题其实挺典型的,根本原因出在前端加密输出的格式和后端解密的预期不匹配。CryptoJS 默认的 AES 加密输出是 Base64 编码的字符串,但它内部包含的内容不仅仅是密文,还包括随机生成的 salt(如果你没显式控制的话),但更关键的是——你在用 ECB 模式时,没有指定 padding,而且 CryptoJS 的默认 padding 是 Pkcs7,不是 PKCS5。

虽然 Pkcs7 和 PKCS5 在块大小为 8 字节时表现一样,但严格来说 Java 默认用的是 PKCS5Padding,处理 16 字节块的时候会有差异。更重要的是,你前端代码里压根没声明 padding,靠默认行为很容易出问题。

另外一个坑是:CryptoJS.AES.encrypt() 返回的是一个 CipherParams 对象,调用 .toString() 时会序列化成 Base64 字符串,这个字符串其实是 salt + ciphertext 的组合(即使 ECB 不需要 salt,CryptoJS 仍可能加上),导致后端拿到的数据结构对不上。

解决方案很简单,按规范来:

第一,明确指定 padding 为 Pkcs7(对应 Java 的 PKCS5Padding 实际能兼容)
第二,不要依赖默认输出,手动提取原始 ciphertext 并转成 Base64
第三,确保密钥长度合法(建议用 16/24/32 字节)

改一下前端代码:

handleSubmit = () => {
const data = JSON.stringify(this.state.formData);
const key = CryptoJS.enc.Utf8.parse('my-secret-key-123'.padEnd(16, '')); // 补齐16字节
const encrypted = CryptoJS.AES.encrypt(data, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
iv: '' // ECB 不需要 IV,但有些版本要求传空
});

// 只取 ciphertext 并转成 Base64 发给后端
const cipherBase64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64);

fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ encryptedData: cipherBase64 })
});
}


然后后端 Java 那边 decode 这个 Base64 后直接解密就行,别再对整个包装结构做解析。

记住一点:前后端加解密通信时,数据格式必须精确一致,不能依赖库的高层封装默认行为。ECB 虽然简单但危险,生产环境建议换 CBC 或 GCM,带上 proper IV。
点赞 1
2026-02-10 14:08
鑫丹酱~
我之前也遇到过类似的问题。问题出在CryptoJS的AES加密默认使用的是Pkcs7Padding填充方式,而Java后端如果声明的是PKCS5Padding,虽然本质上两者是相同的,但有些严格的解密库会校验这个标识。另外ECB模式在不同平台间也可能存在兼容性问题。

你至少需要做三处调整:

1. 明确指定填充方式,前端加密时用CryptoJS.pad.Pkcs7:

const encrypted = CryptoJS.AES.encrypt(
data,
'my-secret-key-123',
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}
).toString();


2. 检查密钥生成方式。有些Java实现要求密钥必须是SecretKeySpec类型,类似这样:

SecretKey key = new SecretKeySpec("my-secret-key-123".getBytes(), "AES");


3. 建议别用ECB模式,改CBC模式更安全。前端指定iv(可以固定,但建议随机生成并传输):
const iv = CryptoJS.enc.Utf8.parse('1234567890123456');
const encrypted = CryptoJS.AES.encrypt(
data,
'my-secret-key-123',
{
iv: iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
}
).toString();


另外注意编码问题,前后端统一用Utf8处理字符串。建议前端加密前不要用JSON.stringify套太多层,先序列化一次就行。这些改动后应该就能正常解密了。

ECB模式本身就有缺陷,建议还是换成CBC。如果数据量大的话,后面可能会遇到更诡异的问题。
点赞 4
2026-02-04 02:07