必须加OPENSSL_RAW_DATA标志,且需base64_decode($sign, true)后再解密;session_key和iv解码后均须为16字节;解密结果需rtrim清理PKCS#7填充再json_decode。

ThinkPHP里用openssl_decrypt解密encryptedData必须加OPENSSL_RAW_DATA
直接调用openssl_decrypt却返回false,八成是漏了OPENSSL_RAW_DATA标志。字节跳动的encryptedData是Base64编码后的密文,但openssl_decrypt默认期望的是原始二进制数据——所以你得先base64_decode($sign, true),再传给函数,并显式带上OPENSSL_RAW_DATA,否则它会自动尝试PKCS#7去填充校验,结果必然失败。
常见错误写法:openssl_decrypt($sign, 'AES-128-CBC', base64_decode($key), 0, base64_decode($iv))
正确写法:openssl_decrypt(base64_decode($sign, true), 'AES-128-CBC', base64_decode($key), OPENSSL_RAW_DATA, base64_decode($iv))
session_key和iv必须严格Base64解码,且长度要刚好16字节
session_key解码后必须是16字节(AES-128要求),iv也必须是16字节。如果base64_decode后长度不对,openssl_decrypt会静默失败,不报错也不返回有效值。
- 用
strlen(base64_decode($key)) !== 16做前置校验,避免后续解密白忙活 -
iv同理,strlen(base64_decode($iv)) !== 16就直接中断,别往下走 - 注意:
base64_decode第二个参数设为true能更严格处理非法字符,减少静默截断风险
解密结果是JSON字符串,但可能含不可见控制字符
openssl_decrypt返回的是原始字节流,看起来像JSON,实际开头或结尾可能混入零字节(\x00)或PKCS#7填充残留。直接json_decode会失败,返回null。
立即学习“PHP免费学习笔记(深入)”;
务必先清理:
- 用
rtrim($decrypted, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")去掉PKCS#7填充 - 再用
json_decode($cleaned, true)解析,检查json_last_error()是否为JSON_ERROR_NONE - 典型结构是
{"phoneNumber":"138xxxxxxxx","purePhoneNumber":"138xxxxxxxx","countryCode":"86"},字段名大小写不能错
ThinkPHP请求参数没过滤干净会导致解密失败
前端传来的code、iv、encryptedData如果带空格、换行、全角字符或URL编码未还原,base64_decode会返回false,进而让整个解密链路中断。
建议在input()之后立即清洗:
$code = trim(input('code'));$iv = str_replace([' ', "\n", "\r", "\t"], '', input('iv'));$encryptedData = str_replace([' ', "\n", "\r", "\t"], '', input('encryptedData'));- 别依赖前端传来的
appid做鉴权,服务端硬编码或配置读取更安全
最常被忽略的一点:字节跳动的jscode2session接口返回的session_key本身也是Base64字符串,但它可能含+、/、=,而某些HTTP代理或Nginx配置会悄悄转义这些字符——解密前务必确认拿到的session_key和原始响应完全一致。



















