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

将C++ OpenSSL AES-CBC解密转Node.js后解密异常,原因何在?

问题:C++ AES-CBC解密转Node.js后结果不符

我将一段C++/Qt的AES-CBC解密代码转为Node.js版本,但解密结果不正确。已确认publicKey、iv、密文内容均正确,推测问题出在Node.js的密钥导入或解密逻辑中。C++代码输出的paddingLength为16,Node.js版本输出为4。

原C++代码(简化版)

AES_KEY *aesKey = new AES_KEY;

AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(publicKey.data()), 256, aesKey);

QByteArray iv =  encryptedFile.read(AES_BLOCK_SIZE);

int blocksPerLoop = 10000;

QByteArray cipher = encryptedFile.read(AES_BLOCK_SIZE * (blocksPerLoop + 1));

int sizeOfInput = cipher.size();

unsigned char *content;

content = new unsigned char[sizeOfInput];
memset(content, 0, sizeof(sizeOfInput));

AES_cbc_encrypt(reinterpret_cast<const unsigned char*>(cipher.data()),
                content,
                size_t(sizeOfInput),
                aesKey,
                reinterpret_cast<unsigned char*>(iv.data()),
                AES_DECRYPT);

int paddingLength = content[sizeOfInput - 1];

qDebug() << "paddingLength" << paddingLength; // prints: 16

转换后的Node.js代码(存在问题)

const aesKey = await crypto.subtle.importKey('raw', Buffer.from(publicKey, 'hex'), { 
    name: 'AES-CBC', 
    length: 256 
}, false, [
    'encrypt', 
    'decrypt'
])

let iv = Buffer.allocUnsafe(AES_BLOCK_SIZE)
await encryptedFile.read(iv, 0, AES_BLOCK_SIZE)

const blocksPerLoop = 10000

const cipher = Buffer.allocUnsafe(AES_BLOCK_SIZE * (blocksPerLoop + 1))
await encryptedFile.read(cipher, 0, AES_BLOCK_SIZE * (blocksPerLoop + 1))

const sizeOfInput = cipher.length

var decipher = crypto.createDecipheriv('AES-256-CBC', aesKey, iv)
decipher.setAutoPadding(false)
const content = Buffer.concat([decipher.update(cipher), decipher.final()])

const paddingLength = content[sizeOfInput - 1]

console.log('paddingLength', paddingLength) // prints: 4

问题排查与修正方案

核心问题

  1. 密钥导入方式不兼容:Node.js内置crypto模块和Web Crypto API(crypto.subtle)的密钥对象不能混用。crypto.createDecipheriv需要直接传入Buffer格式的密钥,而非CryptoKey对象。
  2. 文件读取未校验完整性:encryptedFile.read返回实际读取的字节数,原代码未校验是否读取到完整的IV和密文,可能导致数据截断或包含缓冲区垃圾数据。
  3. 密文长度处理错误:直接使用缓冲区总长度而非实际读取的字节数,会导致解密时处理无效数据。

修正后的Node.js代码

const crypto = require('crypto');
const AES_BLOCK_SIZE = 16;

// 直接将密钥转为Buffer,无需通过crypto.subtle导入
const aesKeyBuffer = Buffer.from(publicKey, 'hex');

// 读取IV并校验完整性
let iv = Buffer.alloc(AES_BLOCK_SIZE);
const ivBytesRead = await encryptedFile.read(iv, 0, AES_BLOCK_SIZE);
if (ivBytesRead !== AES_BLOCK_SIZE) {
    throw new Error('无法读取完整的IV');
}

const blocksPerLoop = 10000;
const cipherBufferSize = AES_BLOCK_SIZE * (blocksPerLoop + 1);
const cipher = Buffer.alloc(cipherBufferSize);
// 读取密文并校验完整性
const cipherBytesRead = await encryptedFile.read(cipher, 0, cipherBufferSize);
if (cipherBytesRead === 0) {
    throw new Error('未读取到密文数据');
}

// 使用Buffer格式密钥初始化解密器
const decipher = crypto.createDecipheriv('AES-256-CBC', aesKeyBuffer, iv);
decipher.setAutoPadding(false); // 与C++逻辑一致,手动处理填充
// 仅解密实际读取到的密文部分
const content = Buffer.concat([
    decipher.update(cipher.slice(0, cipherBytesRead)), 
    decipher.final()
]);

const paddingLength = content[content.length - 1];
console.log('paddingLength', paddingLength); // 应输出16

关键修正点

  • 移除crypto.subtle.importKey,直接使用Buffer.from生成密钥缓冲区,适配Node.jscrypto模块的要求。
  • 校验文件读取的字节数,确保IV和密文读取完整,避免数据不完整导致解密错误。
  • 解密时仅处理实际读取到的密文数据,排除缓冲区中未被覆盖的垃圾数据。

内容的提问来源于stack exchange,提问作者André Fiedler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:32:44