React表单提交时用AES加密数据,后端返回解密失败怎么办?
我在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里有问题?
虽然 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 字节)
改一下前端代码:
然后后端 Java 那边 decode 这个 Base64 后直接解密就行,别再对整个包装结构做解析。
记住一点:前后端加解密通信时,数据格式必须精确一致,不能依赖库的高层封装默认行为。ECB 虽然简单但危险,生产环境建议换 CBC 或 GCM,带上 proper IV。
你至少需要做三处调整:
1. 明确指定填充方式,前端加密时用CryptoJS.pad.Pkcs7:
2. 检查密钥生成方式。有些Java实现要求密钥必须是SecretKeySpec类型,类似这样:
3. 建议别用ECB模式,改CBC模式更安全。前端指定iv(可以固定,但建议随机生成并传输):
另外注意编码问题,前后端统一用Utf8处理字符串。建议前端加密前不要用JSON.stringify套太多层,先序列化一次就行。这些改动后应该就能正常解密了。
ECB模式本身就有缺陷,建议还是换成CBC。如果数据量大的话,后面可能会遇到更诡异的问题。