无法用Node.js crypto解密OpenSSL AES-256-CBC加密内容
Bash AES-256-CBC加密内容的Node.js解密方案
我用OpenSSL命令对字符串进行AES-256-CBC加密和解密完全正常:
ENCRYPTED=$(echo "my_secret_data" | openssl aes-256-cbc -pass "pass:bab3fd92bcd7d464" -pbkdf2 -a -A) echo -n $ENCRYPTED | base64 -d | openssl aes-256-cbc -d -pass "pass:bab3fd92bcd7d464" -pbkdf2
但用Node.js的crypto模块解密时,执行以下代码报错:
const crypto = require('crypto'); const encryptedTextBase64 = 'U2FsdGVkX18AYE13z9uboo3WZhktr03EeV0WFA0MH4o='; const password = 'bab3fd92bcd7d464'; // Decode the base64-encoded text const encryptedText = Buffer.from(encryptedTextBase64, 'base64'); // Create a decipher object const decipher = crypto.createDecipher('aes-256-cbc', password); // Update the decipher with the encrypted text let decrypted = decipher.update(encryptedText, 'binary', 'utf8'); decrypted += decipher.final('utf8'); console.log(decrypted);
错误信息:
node:internal/crypto/cipher:199 const ret = this[kHandle].final(); ^ Error: error:1C800064:Provider routines::bad decrypt at Decipher.final (node:internal/crypto/cipher:199:29) at Object.<anonymous> (.../decrypt.js:14:23) at Module._compile (node:internal/modules/cjs/loader:1256:14) at Module._extensions..js (node:internal/modules/cjs/loader:1310:10) at Module.load (node:internal/modules/cjs/loader:1119:32) at Module._load (node:internal/modules/cjs/loader:960:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:86:12) at node:internal/main/run_main_module:23:47 { library: 'Provider routines', reason: 'bad decrypt', code: 'ERR_OSSL_BAD_DECRYPT' }
目标是实现Bash加密、Node.js解密的兼容。
问题原因
OpenSSL的aes-256-cbc命令默认生成带盐值的特殊格式(开头固定为Salted__),且使用PBKDF2推导密钥时的默认参数(迭代次数10000、哈希算法SHA-256)与Node.js旧APIcreateDecipher的默认逻辑不匹配,导致密钥/IV推导不一致,触发解密失败。
解决方案
正确的Node.js解密代码
手动解析OpenSSL加密格式中的盐值,用PBKDF2推导匹配的密钥和IV,再调用createDecipheriv完成解密:
const crypto = require('crypto'); const encryptedTextBase64 = 'U2FsdGVkX18AYE13z9uboo3WZhktr03EeV0WFA0MH4o='; const password = 'bab3fd92bcd7d464'; // 解码base64并提取OpenSSL格式中的盐值和密文 const encryptedBuffer = Buffer.from(encryptedTextBase64, 'base64'); const salt = encryptedBuffer.slice(8, 16); // 前8字节为固定标识"Salted__",后续8字节是盐值 const ciphertext = encryptedBuffer.slice(16); // 用PBKDF2推导密钥和IV,匹配OpenSSL默认参数 const keyIv = crypto.pbkdf2Sync(password, salt, 10000, 48, 'sha256'); const key = keyIv.slice(0, 32); // AES-256需要32字节密钥 const iv = keyIv.slice(32, 48); // CBC模式需要16字节IV // 执行解密 const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(ciphertext, 'binary', 'utf8'); decrypted += decipher.final('utf8'); console.log(decrypted); // 输出: my_secret_data
可选:调整Bash加密命令(如需更可控格式)
如果不想处理OpenSSL默认格式,可在加密时显式指定盐值和IV,但需额外保存这些参数用于解密。不过保持原加密命令即可,上述Node.js代码已完全兼容默认格式。
内容的提问来源于stack exchange,提问作者Miguel Prytoluk
相关产品推荐
相关产品推荐

