php与js用aes互通必须统一参数:cbc模式、显式16字节iv、严格16/24/32字节密钥、密文base64编码传输,否则必解密失败。

PHP和JS用AES互通必须统一openssl_encrypt与CryptoJS.AES.encrypt的参数细节
不统一就必然解密失败,常见报错是Malformed UTF-8 data或空字符串。核心矛盾不在语言差异,而在默认行为不同:PHP的openssl_encrypt默认用PKCS#7填充、ECB/CBC模式、无IV自动补零;CryptoJS默认用PKCS#7、CBC、随机IV且Base64编码密文+IV拼接。
实操建议:
- 强制双方都用
CBC模式(ECB不安全且JS默认不暴露ECB选项) - PHP侧禁用自动IV生成:
openssl_random_pseudo_bytes(16)生成IV,并显式传入openssl_encrypt第4个参数 - JS侧禁用随机IV:
{ iv: CryptoJS.enc.Utf8.parse('16-byte-iv-here') },且IV必须是16字节Utf8解析结果 - 密钥必须严格16/24/32字节——PHP用
mb_strlen($key, '8bit')校验,JS用CryptoJS.enc.Utf8.parse(key).toString()转成WordArray再检查长度
PHP加密后JS解密失败?先检查openssl_encrypt输出是否被二次编码
PHP默认返回原始二进制密文,而CryptoJS只接受Base64或Hex字符串输入。若直接echo密文到JS,会因不可见字符截断导致解密失败。
正确做法:
立即学习“PHP免费学习笔记(深入)”;
- PHP加密后立刻
base64_encode()密文,再把IV也base64_encode(),拼成$ciphertext.'|'.$iv传给前端 - JS收到后
split('|')拆分,用CryptoJS.enc.Base64.parse()分别解析密文和IV - 切勿在PHP里用
bin2hex()——CryptoJS的enc.Hex解析逻辑与PHP hex输出字节序不一致,容易出错
CryptoJS解密时抛Invalid AES key length?密钥字节长度没对齐
CryptoJS要求密钥最终为WordArray,且长度必须是128/192/256位(即16/24/32字节)。如果JS里直接传字符串'my-key',它会被按UTF-8编码后转WordArray,但PHP侧若用md5($key)或sha1($key)生成固定长度密钥,两边哈希算法不一致就会错。
稳妥方案:
- PHP侧用
hash('sha256', $raw_key, true)生成32字节密钥(true表示返回原始二进制) - JS侧用
CryptoJS.SHA256(CryptoJS.enc.Utf8.parse(raw_key)).toString(CryptoJS.enc.Base64)再CryptoJS.enc.Base64.parse()还原——但更简单的是PHP把hash('sha256', $raw_key, true)结果base64_encode后传给JS,JS直接CryptoJS.enc.Base64.parse() - 避免用
substr(md5(), 0, 16)这类截断操作——MD5是16字节,但PHP的md5()默认返回32字符hex字符串,截取前16字符只是8字节
为什么用openssl_encrypt却总遇到data not padded错误?
这个错误实际是CryptoJS解密时发现PKCS#7填充不合法,根源常在PHP侧:你可能用了OPENSSL_ZERO_PADDING但JS没同步关闭填充,或者PHP加密后被gzip压缩、JSON编码等中间环节破坏了二进制完整性。
排查重点:
- 确认PHP加密调用中未传
OPENSSL_ZERO_PADDING标志(除非JS也显式配置{ padding: CryptoJS.pad.NoPadding }) - 确保传输过程无额外编码:比如AJAX发送时
contentType: 'application/json'会导致PHP自动json_decode并可能丢弃二进制数据,改用application/x-www-form-urlencoded或text/plain - 前端接收响应时,用
responseType: 'text'而非'json',避免浏览器自动解析破坏Base64字符串











