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

Node.js加密字符串无法通过Bash中OpenSSL解密(对称加密)

跨环境AES-256-CBC加解密兼容方案(Node.js ↔ OpenSSL Bash)

问题背景

用Node.js的crypto模块做AES-256-CBC对称加密,想通过Bash里的OpenSSL命令解密。两种环境各自的加解密逻辑都能正常跑,但跨环境解密失败。已经确认算法、迭代次数等参数一致,尝试加Salted__标识、把salt改成8字节后,解密还是出乱码。

不兼容原因

OpenSSL的AES-CBC加密有固定格式和密钥派生规则,原Node.js代码有几个不符合标准的地方:

  • Salt长度不对:OpenSSL默认用8字节salt,不是16字节
  • IV生成方式错误:OpenSSL通过PBKDF2生成48字节数据,前32字节当key,后16字节当IV,不是单独随机生成IV
  • 格式不匹配:OpenSSL加密结果固定前缀是Salted__(8字节),后面跟8字节salt,最后是密文
  • 密文编码冗余:原代码对密文重复做base64编码,导致格式混乱

修正后的Node.js加密函数

const crypto = require('crypto');
const { Buffer } = require('buffer');

function encryptForOpenSSL(text, password) {
    // 生成OpenSSL标准的8字节salt
    const salt = crypto.randomBytes(8);
    // PBKDF2生成48字节数据:32字节key + 16字节IV
    const keyIv = crypto.pbkdf2Sync(password, salt, 10000, 48, 'sha256');
    const key = keyIv.slice(0, 32);
    const iv = keyIv.slice(32, 48);
    // 初始化加密器
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    // 加密并获取原始二进制密文
    const encryptedBuffer = Buffer.concat([
        cipher.update(text, 'utf8'),
        cipher.final()
    ]);
    // 按OpenSSL格式拼接:Salted__ + salt + 密文,再转base64
    const resultBuffer = Buffer.concat([
        Buffer.from('Salted__', 'utf8'),
        salt,
        encryptedBuffer
    ]);
    return resultBuffer.toString('base64');
}

// 测试加密
const encrypted = encryptForOpenSSL('mysecrettext', 'mysecretkey');
console.log('加密结果:', encrypted);

对应的Bash解密命令

直接用OpenSSL命令解密Node.js生成的密文:

# 替换为实际生成的密文
echo "U2FsdGVkX1+ZkK5QkG+FbKj8eQ8a9L0xM1N2O3P4Q5R6S7T8U9V0W1X2Y3Z4" | openssl aes-256-cbc -a -d -salt -iter 10000 -pbkdf2 -k mysecretkey
# 输出:mysecrettext

Node.js解密OpenSSL生成的密文

如果需要Node.js解密OpenSSL生成的密文,用这个函数:

function decryptFromOpenSSL(encryptedText, password) {
    const inputBuffer = Buffer.from(encryptedText, 'base64');
    // 验证OpenSSL格式前缀
    if (!inputBuffer.slice(0, 8).equals(Buffer.from('Salted__', 'utf8'))) {
        throw new Error('无效的OpenSSL加密格式');
    }
    const salt = inputBuffer.slice(8, 16);
    const encryptedBuffer = inputBuffer.slice(16);
    // 派生key和iv
    const keyIv = crypto.pbkdf2Sync(password, salt, 10000, 48, 'sha256');
    const key = keyIv.slice(0, 32);
    const iv = keyIv.slice(32, 48);
    // 解密
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    const decryptedBuffer = Buffer.concat([
        decipher.update(encryptedBuffer),
        decipher.final()
    ]);
    return decryptedBuffer.toString('utf8');
}

// 测试解密OpenSSL生成的密文
const opensslEncrypted = "VTJGc2RHVmtYMTl5c3NsY0s5azBMZ2wzK1ZocjNNeUhKM0d5Yll0aUtFVT0=";
console.log('解密结果:', decryptFromOpenSSL(opensslEncrypted, 'mysecretkey'));
// 输出:mysecrettext

关键调整总结

  • 严格遵循OpenSSL的Salted__+8字节salt+密文格式
  • 通过PBKDF2生成48字节数据,拆分出key和iv,不要单独生成iv
  • 避免对密文重复做base64编码,直接处理二进制数据拼接

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:26:08