Node.js AES加解密重启进程后报ERR_OSSL_EVP_BAD_DECRYPT如何解决
ERR_OSSL_EVP_BAD_DECRYPT错误含义
该错误是OpenSSL底层库抛出的解密失败错误,核心原因是解密时使用的密钥、IV和加密时使用的参数不匹配,或是密文被篡改,导致解密后的填充校验不通过。
问题根源
你最初的代码在进程启动阶段生成了随机salt:
const salt = crypto.randomBytes(16); const key = crypto.scryptSync("Pa$$w0rd", salt, 32);
每次重启Node.js进程salt都会重新随机生成,用该salt派生出来的AES密钥和加密时使用的密钥完全不同,自然无法解密之前生成的密文。
符合加密规范的实现方案
你在编辑4中调整的实现是符合行业规范的,核心逻辑是:
- salt和IV都不属于敏感信息,不需要加密存储,可以直接和密文拼接后存入数据库/文件
- 唯一需要保密的是加密口令,建议存到环境变量中,不要硬编码在代码里
可以参考以下优化后的实现,规避分隔符冲突问题,同时提升加密强度:
const crypto = require("crypto"); const algorithm = "aes-256-cbc"; // 加密函数 function encrypt(text, password) { const iv = crypto.randomBytes(16); const salt = crypto.randomBytes(16); // 提升scrypt密钥派生强度,可根据业务性能要求调整参数 const key = crypto.scryptSync(password, salt, 32, { N: 16384, r: 8, p: 1 }); const cipher = crypto.createCipheriv(algorithm, key, iv); let encrypted = cipher.update(text, "utf8", "hex"); encrypted += cipher.final("hex"); // 按固定长度拼接:32位IV hex + 32位salt hex + 密文hex,不需要分隔符 return iv.toString("hex") + salt.toString("hex") + encrypted; } // 解密函数 function decrypt(encryptedStr, password) { // 按固定长度拆分 const iv = Buffer.from(encryptedStr.slice(0, 32), "hex"); const salt = Buffer.from(encryptedStr.slice(32, 64), "hex"); const data = encryptedStr.slice(64); const key = crypto.scryptSync(password, salt, 32, { N: 16384, r: 8, p: 1 }); const decipher = crypto.createDecipheriv(algorithm, key, iv); let decrypted = decipher.update(data, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } // 使用示例 const PASSWORD = process.env.PASSWORD || "你的安全口令"; const encrypted = encrypt("需要加密的敏感文本", PASSWORD); console.log("加密结果:", encrypted); const decrypted = decrypt(encrypted, PASSWORD); console.log("解密结果:", decrypted);
额外安全建议
- 生产环境的加密口令复杂度要足够高,不要使用弱口令
- 若对安全性要求更高,可以额外对拼接后的密文计算HMAC校验值,解密前先校验密文是否被篡改
- Node.js 10+版本scrypt函数才稳定,低版本环境需要做兼容处理
内容的提问来源于stack exchange,提问作者Marc
相关产品推荐
相关产品推荐

