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
相关产品推荐
相关产品推荐

