NodeJS转PHP:Blowfish CBC解密代码转换遇阻求解决方案
我帮你梳理了Node.js和PHP代码之间的几个核心差异,这些就是导致解密结果不一致的关键原因:
1. 自动填充(Padding)的处理差异
Node.js代码里明确调用了cipher.setAutoPadding(false),这意味着解密后不会自动移除任何填充字节;但PHP的openssl_decrypt默认会启用PKCS#7填充的自动移除,这会直接导致解密结果出现偏差。你必须在PHP中显式关闭自动填充处理。
2. 输入输出的编码对应问题
Node.js中cipher.update(chunk, 'binary', 'hex')是把原始二进制格式的输入数据解密后输出为十六进制字符串;而你的PHP代码里用了hex2bin($chunk),如果$chunk是原始二进制数据的话,这一步转码完全错误。另外,Node.js的cipher.final()会沿用update指定的输出编码(这里是十六进制),所以最终输出是hex字符串;PHP的openssl_decrypt如果用OPENSSL_RAW_DATA参数,会输出二进制数据,需要手动转成hex才能和Node.js的输出对齐。
3. 密钥的兼容性(潜在坑)
早期Node.js版本(v10之前)中,crypto.createDecipheriv如果传入的密钥长度不符合Blowfish要求(32-448位,即4-56字节),会自动对密钥做MD5哈希来生成合法密钥;但PHP的openssl_decrypt会直接使用原始密钥,长度不合法的话会报错或产生错误结果。确保你的密钥长度在合法范围内,或者如果用的是旧版Node.js,需要在PHP中对密钥做同样的MD5处理。
修正后的PHP代码
分两种情况处理:
情况1:$chunk是原始二进制数据(和Node.js读取的文件二进制一致)
// 解密得到二进制结果,关闭自动填充 $decryptedBinary = openssl_decrypt( $chunk, 'bf-cbc', $blowfishKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, // 对应Node的setAutoPadding(false) hex2bin('0001020304050607') // IV和Node保持一致,转成二进制 ); // 如果需要和Node.js输出的十六进制字符串一致,转成hex格式 $decryptedHex = bin2hex($decryptedBinary); echo $decryptedHex;
情况2:$chunk是十六进制字符串(比如从Node.js输出拿到的)
// 先把hex转成二进制 $chunkBinary = hex2bin($chunk); $decryptedBinary = openssl_decrypt( $chunkBinary, 'bf-cbc', $blowfishKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, hex2bin('0001020304050607') ); $decryptedHex = bin2hex($decryptedBinary); echo $decryptedHex;
额外验证建议
- 确认Node.js和PHP使用的密钥完全一致(包括编码,比如是否是UTF-8字符串或原始二进制)。
- 如果你用的是Node.js v10及以上版本,
createDecipheriv不再自动处理密钥,和PHP行为一致,这时候只需要保证填充和编码对应即可。 - 如果解密结果末尾有多余的空字节,那是因为原加密用了零填充,Node.js关闭自动填充后会保留这些字节,PHP也会保留,你可以根据业务需求手动去除。
内容的提问来源于stack exchange,提问作者user7963368

