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

NodeJS AES-256-CBC加密后,Linux下OpenSSL解密出现前置乱码求助

AES-256-CBC加密后OpenSSL解密明文前出现乱码的解决方法

我在NodeJS中编写了AES-256-CBC加密代码,使用PBKDF2派生密钥,添加Salted__前缀生成Base64密文。将该密文放到Linux服务器执行OpenSSL解密命令时,虽能得到明文“its a secret!”,但明文前出现乱码,请问如何解决?


相关原始代码与输出

NodeJS加密代码

const crypto = require('crypto');
const passphrase = 'test';
const iterations = 100000; 
const keyLength = 32;
const saltLength = 8; 
const ivLength = 16;

let salt = crypto.randomBytes(saltLength);
let key = crypto.pbkdf2Sync(passphrase, salt, iterations, keyLength, 'sha256');
let iv = crypto.randomBytes(ivLength);
console.log("Salt size: ",salt.length + " bytes");
console.log("Key size: ",key.length + " bytes");
console.log("IV size: ",iv.length +" bytes");
const dataToEncrypt = 'its a secret!';
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encryptedData = cipher.update(dataToEncrypt, 'utf8', 'base64');
encryptedData += cipher.final('base64');
const saltedPrefix = Buffer.from('Salted__');
const prefixedData = Buffer.concat([saltedPrefix, salt, iv, Buffer.from(encryptedData, 'base64')]);
console.log("Result HEX: ", prefixedData.toString('hex'));
const encrypted = prefixedData.toString('base64');
/* FINAL SIZE */
console.log("Result size: ", encrypted.length +" bytes");

/* RESULT LOG */ 
console.log('Base64 encrypted: ' + encrypted);

NodeJS输出

Salt size:  8 bytes
Key size:  32 bytes
IV size:  16 bytes
Result HEX:  53616c7465645f5f2d920c9fc07a1131ff26f94a8e2b8110691e022c824f9251256f28e1533a4dfcb7eda2ae5f665809
Result size:  64 bytes
Base64 encrypted: U2FsdGVkX18tkgyfwHoRMf8m+UqOK4EQaR4CLIJPklElbyjhUzpN/Lftoq5fZlgJ

Linux解密命令

echo "U2FsdGVkX18tkgyfwHoRMf8m+UqOK4EQaR4CLIJPklElbyjhUzpN/Lftoq5fZlgJ" | (openssl enc -AES-256-cbc -d -a -pass "pass:test" -pbkdf2 -iter 100000 -md sha256 -p; echo) | tee >(hexdump -C)

解密输出

salt=2D920C9FC07A1131
key=C4B6F8A9A1DA258846E066D8B3D4A7028922376D87DC898255C8DE64DD994E09
iv =F7369730119C2461AB0A6CA1B8D3015D
▒▒f/B>▒=O▒▒琰its a secret!
00000000  73 61 6c 74 3d 32 44 39  32 30 43 39 46 43 30 37  |salt=2D920C9FC07|
00000010  41 31 31 33 31 0a 6b 65  79 3d 43 34 42 36 46 38  |A1131.key=C4B6F8|
00000020  41 39 41 31 44 41 32 35  38 38 34 36 45 30 36 36  |A9A1DA258846E066|
00000030  44 38 42 33 44 34 41 37  30 32 38 39 32 32 33 37  |D8B3D4A702892237|
00000040  36 44 38 37 44 43 38 39  38 32 35 35 43 38 44 45  |6D87DC898255C8DE|
00000050  36 34 44 44 39 39 34 45  30 39 0a 69 76 20 3d 46  |64DD994E09.iv =F|
00000060  37 33 36 39 37 33 30 31  31 39 43 32 34 36 31 41  |7369730119C2461A|
00000070  42 30 41 36 43 41 31 42  38 44 33 30 31 35 44 0a  |B0A6CA1B8D3015D.|
00000080  f6 db 66 2f 01 42 3e 80  3d 4f 9d c5 04 e7 90 b0  |..f/.B>.=O......|
00000090  69 74 73 20 61 20 73 65  63 72 65 74 21 0a        |its a secret!.|
0000009e

问题原因

你在NodeJS代码里错误地将IV直接追加到了Salted__前缀和salt之后,但OpenSSL的enc命令在使用PBKDF2时,会自动从密钥派生过程中生成IV(对于AES-256-CBC,PBKDF2会生成32字节密钥+16字节IV,共48字节的派生数据)。你额外添加的IV会被OpenSSL当作密文的一部分解密,从而在明文开头产生乱码。

OpenSSL的标准加密格式是:Salted__ + 8字节salt + 密文,不需要额外存储IV——IV是和密钥一起从PBKDF2派生出来的。


解决方案

修复后的NodeJS加密代码

修改代码,移除对IV的手动存储,改为从PBKDF2派生数据中提取IV,只保留Salted__前缀、salt和密文:

const crypto = require('crypto');
const passphrase = 'test';
const iterations = 100000; 
const keyLength = 32;
const saltLength = 8; 

let salt = crypto.randomBytes(saltLength);
// PBKDF2派生48字节数据:32字节密钥 + 16字节IV
let keyIv = crypto.pbkdf2Sync(passphrase, salt, iterations, keyLength + 16, 'sha256');
let key = keyIv.slice(0, keyLength);
let iv = keyIv.slice(keyLength); // 从派生数据中提取IV
console.log("Salt size: ",salt.length + " bytes");
console.log("Key size: ",key.length + " bytes");
console.log("IV size: ",iv.length +" bytes");

const dataToEncrypt = 'its a secret!';
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encryptedData = cipher.update(dataToEncrypt, 'utf8', 'base64');
encryptedData += cipher.final('base64');

const saltedPrefix = Buffer.from('Salted__');
// 只拼接前缀、salt和密文,不再加入IV
const prefixedData = Buffer.concat([saltedPrefix, salt, Buffer.from(encryptedData, 'base64')]);
console.log("Result HEX: ", prefixedData.toString('hex'));
const encrypted = prefixedData.toString('base64');
console.log("Result size: ", encrypted.length +" bytes");
console.log('Base64 encrypted: ' + encrypted);

验证解密结果

用原OpenSSL命令解密修复后的密文,就能得到无乱码的明文:

echo "修复后的Base64密文" | (openssl enc -AES-256-cbc -d -a -pass "pass:test" -pbkdf2 -iter 100000 -md sha256 -p; echo) | tee >(hexdump -C)

额外说明

  • OpenSSL的enc工具在使用PBKDF2时,默认会从passphrase和salt派生密钥长度 + IV长度的字节数,不需要手动传递IV参数,也不需要在密文中存储IV。
  • 若非要手动指定IV(自定义格式),需在OpenSSL解密时加上-iv参数传入对应IV值,但这不符合OpenSSL标准加密格式,不推荐。

内容的提问来源于stack exchange,提问作者Daniel Morais

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 17:07:03