You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:25:37