PHP加密与前端JS互通_统一AES密钥模式编解码【说明】

2次阅读

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

PHP加密与前端JS互通_统一AES密钥模式编解码【说明】

PHP和JS用AES互通必须统一openssl_encryptCryptoJS.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-urlencodedtext/plain
  • 前端接收响应时,用responseType: 'text'而非'json',避免浏览器自动解析破坏Base64字符串

密钥派生、IV传递、编码链路这三处任何一个环节字节级不一致,都会让AES变成单向黑洞。调试时优先比对PHP输出的Base64密文和JS输入的Base64密文是否完全一致(包括换行、空格),再逐层往回查。

text=ZqhQzanResources