Node.js中AES-256-CBC加解密报Invalid key length错误求助
解决Node.js中AES-256-CBC密钥长度无效错误并兼容PHP实现
问题场景
需要使用给定密钥通过AES-256-CBC算法加密数据,PHP代码可正常运行并得到预期结果,但Node.js使用crypto库时抛出 "RangeError: Invalid key length" 错误,且结果无法与PHP兼容。
可正常运行的PHP代码及示例
// 示例参数 $key = "9v6ZyFBzNYoP2Un8H5cZq5FeBwxL6itqNZsm7lisGBQ="; $text = "Hello, Help me with this"; // 预期输出:w8mBJyHzQ3yJlkSvAX8t3qy9GVaUvBiOBAPFDXOzFMQ=" // 加密函数 function encrypt($text, $key, $size = 16) { $pad = $size - (strlen($text) % $size); $padtext = $text . str_repeat(chr($pad), $pad); $crypt = openssl_encrypt($padtext, "AES-256-CBC", base64_decode($key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, '0123456789abcdef'); return base64_encode($crypt); } // 解密函数 function decrypt($crypt, $key) { $padtext = openssl_decrypt(base64_decode($crypt), "AES-256-CBC", base64_decode($key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, '0123456789abcdef'); $pad = ord($padtext[strlen($padtext) - 1]); if ($pad > strlen($padtext)) { return false; } if (strspn($padtext, $padtext[strlen($padtext) - 1], strlen($padtext) - $pad) != $pad) { $text = "Error"; } $text = substr($padtext, 0, -1 * $pad); return $text; }
报错的Node.js代码
const crypto = require('crypto'); // 示例参数 const key = "9v6ZyFBzNYoP2Un8H5cZq5FeBwxL6itqNZsm7lisGBQ="; const data = "Hello, Help me with this"; // 执行时抛出:RangeError: Invalid key length function encrypt(data, key) { try { const cipher = crypto.createCipheriv("aes-256-cbc", atob(key), '0123456789abcdef'); return cipher.update(data, 'utf-8', 'base64') + cipher.final('base64'); } catch (error) { console.log(error); return error } } function decrypt(enc_data, key) { try { const decipher = crypto.createDecipheriv("aes-256-cbc", atob(key), '0123456789abcdef'); return (decipher.update(enc_data, 'base64', 'utf-8') + decipher.final('utf-8')); } catch (error) { console.log(error); return error } }
错误原因分析
- 密钥处理错误:Node.js中使用
atob()解码base64密钥,会将二进制数据转换为UTF-8字符串,若密钥包含非UTF-8可表示的字节,会导致数据损坏,进而触发密钥长度无效错误。正确做法是直接获取原始二进制Buffer。 - 填充逻辑不匹配:PHP代码手动实现了PKCS#7填充,并通过
OPENSSL_ZERO_PADDING禁用自动填充;而Node.js的createCipheriv默认启用PKCS#7填充,会导致双重填充,结果无法兼容。
修正后的Node.js代码
const crypto = require('crypto'); // PKCS#7填充函数,与PHP填充逻辑一致 function pkcs7Pad(data, blockSize = 16) { const padLength = blockSize - (data.length % blockSize); const pad = Buffer.from(String.fromCharCode(padLength).repeat(padLength), 'utf8'); return Buffer.concat([Buffer.from(data, 'utf8'), pad]); } // PKCS#7去填充函数 function pkcs7Unpad(data) { if (!data || data.length === 0) return data; const padLength = data[data.length - 1]; if (padLength > data.length) return null; return data.slice(0, data.length - padLength); } function encrypt(data, key) { try { const keyBuffer = Buffer.from(key, 'base64'); // 正确解码base64密钥为二进制Buffer const iv = Buffer.from('0123456789abcdef', 'utf8'); // 与PHP一致的IV const cipher = crypto.createCipheriv('aes-256-cbc', keyBuffer, iv); cipher.setAutoPadding(false); // 禁用自动填充,与PHP的OPENSSL_ZERO_PADDING对应 const paddedData = pkcs7Pad(data); const encrypted = Buffer.concat([cipher.update(paddedData), cipher.final()]); return encrypted.toString('base64'); } catch (error) { console.error(error); return error; } } function decrypt(encData, key) { try { const keyBuffer = Buffer.from(key, 'base64'); const iv = Buffer.from('0123456789abcdef', 'utf8'); const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuffer, iv); decipher.setAutoPadding(false); const encryptedBuffer = Buffer.from(encData, 'base64'); const decrypted = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]); const unpaddedData = pkcs7Unpad(decrypted); return unpaddedData ? unpaddedData.toString('utf8') : 'Error'; } catch (error) { console.error(error); return error; } } // 测试验证 const key = "9v6ZyFBzNYoP2Un8H5cZq5FeBwxL6itqNZsm7lisGBQ="; const data = "Hello, Help me with this"; const encrypted = encrypt(data, key); console.log('加密结果:', encrypted); // 输出:w8mBJyHzQ3yJlkSvAX8t3qy9GVaUvBiOBAPFDXOzFMQ= const decrypted = decrypt(encrypted, key); console.log('解密结果:', decrypted); // 输出:Hello, Help me with this
验证说明
修正后的代码:
- 用
Buffer.from(key, 'base64')替代atob(key),确保密钥为原始二进制格式,解决密钥长度错误 - 实现与PHP一致的PKCS#7填充/去填充逻辑,并禁用自动填充,保证加解密结果与PHP完全兼容
内容的提问来源于stack exchange,提问作者Abhineet Kumar
相关产品推荐
相关产品推荐

