You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js解密Linux下OpenSSL加密字符串失败问题排查

问题根源与解决方法

你的解密代码存在两个核心问题,直接导致了空字符串输出:

1. 密文输入格式不匹配

OpenSSL命令中的-a参数指定输出Base64编码的密文,但你在Node.js的decipher.update()里错误地使用了'hex'作为输入编码——这相当于让Node.js把Base64格式的密文当成十六进制字符串解析,自然无法识别有效内容,最终输出空字符串。

2. 解密API的废弃与密钥派生细节

另外,crypto.createDecipher()这个API已经被Node.js官方废弃,更关键的是:OpenSSL的-pass pass:secret会通过EVP_BytesToKey算法从密码派生密钥和IV,并且默认自动添加salt(你看到的密文开头U2FsdGVkX1就是Salted__的Base64编码)。虽然旧的createDecipher()也兼容这个逻辑,但使用createDecipheriv()手动处理salt会更可靠、符合现代开发规范。


正确的解密实现

快速修复版(验证用)

如果你只想快速解决空字符串问题,只需把输入编码从'hex'改成'base64',旧API也能正常工作(但不推荐长期使用):

const crypto = require('crypto');
var decipher = crypto.createDecipher("aes-256-ctr", "secret");
var out = decipher.update("U2FsdGVkX1/5Zhb07x8v0nw7jQ==", 'base64', 'utf8');
out += decipher.final('utf8');
console.log(out); // => 'bar'

规范实现(推荐)

使用未废弃的createDecipheriv(),手动处理salt并派生密钥/IV,逻辑更清晰可控:

const crypto = require('crypto');

// 实现OpenSSL的EVP_BytesToKey算法
function evpBytesToKey(password, salt, keySize = 32, ivSize = 16) {
  let key = Buffer.from('');
  let iv = Buffer.from('');
  let data = Buffer.from('');
  while (key.length < keySize || iv.length < ivSize) {
    data = Buffer.concat([data, Buffer.from(password), salt]);
    const md5Hash = crypto.createHash('md5').update(data).digest();
    data = md5Hash;
    key = Buffer.concat([key, md5Hash.slice(0, keySize - key.length)]);
    if (iv.length < ivSize) {
      iv = Buffer.concat([iv, md5Hash.slice(key.length - md5Hash.length, key.length - md5Hash.length + ivSize - iv.length)]);
    }
  }
  return { key, iv };
}

const encryptedBase64 = "U2FsdGVkX1/5Zhb07x8v0nw7jQ==";
// 解码Base64密文
const encryptedBuffer = Buffer.from(encryptedBase64, 'base64');
// 提取salt:前8字节是固定标识"Salted__",后续8字节是salt
const salt = encryptedBuffer.slice(8, 16);
// 提取实际加密内容
const ciphertext = encryptedBuffer.slice(16);

// 派生密钥和IV
const { key, iv } = evpBytesToKey('secret', salt);
// 创建解密器
const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
// 执行解密
let out = decipher.update(ciphertext, undefined, 'utf8');
out += decipher.final('utf8');

console.log(out); // => 'bar'

总结

  • 最直接的错误是输入编码不匹配,修正编码后即可得到正确结果;
  • 长期开发建议使用createDecipheriv()的规范实现,避免依赖废弃API,同时更清晰地控制加密解密流程。

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

火山引擎 最新活动