Node.js/Express.js后端AES解密失败问题求助
问题描述
从MongoDB获取数据解密时,部分数据仍为加密状态,且最新修改后触发bad decrypt错误。相关代码如下:
数据接收接口
app.get("/recieve", async (req, res) => { try { const data = await UploadData.find(); const decryptedData = data.map((item) => { const decryptedFullName = decryptData(item.fullname, secretKey); const decryptedCatName = decryptData(item.catName, secretKey); const decryptedEmail = decryptData(item.email, secretKey); const decryptedContact = decryptData(item.contact, secretKey); const decryptedLocation = decryptData(item.location, secretKey); return { ...item.toObject(), fullname: decryptedFullName, catName: decryptedCatName, email: decryptedEmail, contact: decryptedContact, location: decryptedLocation, }; }); res.json(decryptedData); } catch (error) { console.error("Error fetching data:", error); res.status(500).json({ error: "Internal server error" }); } });
解密函数
function decryptData(encryptedData, key) { try { const buff = Buffer.from(encryptedData, "base64"); encryptedData = buff.toString("utf-8"); var decipher = crypto.createDecipheriv("aes-256-cbc", key, iv); return ( decipher.update(encryptedData, "base64", "utf8") + decipher.final("utf8") ); } catch (error) { console.log("Error decrypting data:", error); return null; } }
加密函数
function encryptData(data, key) { console.log("Encrypted called"); const keyBuffer = Buffer.from(key, "hex"); const cipher = crypto.createCipheriv("aes-256-cbc", keyBuffer, iv); let encryptedData = cipher.update(data, "utf8", "base64") + cipher.final("base64"); return Buffer.from(encryptedData).toString("base64"); }
密钥与IV生成代码
const crypto = require("crypto"); const secretKey = crypto.randomBytes(32); const iv = crypto.randomBytes(8).toString("hex");
错误原因分析与修复方案
1. IV长度不符合AES-CBC规范
AES-256-CBC要求IV长度为16字节(128位),当前代码生成的是8字节转hex(仅8字节),这会直接导致解密时触发"bad decrypt"错误。
2. 密钥处理逻辑不一致
加密函数中对密钥做了Buffer.from(key, "hex")转换,但解密函数直接使用原始密钥,两者处理逻辑不匹配,导致解密时密钥格式错误。
3. 密钥/IV未持久化
每次服务器重启都会生成新的密钥和IV,导致之前加密的数据永久无法解密。
4. 冗余的双重Base64编码
加密时做了两次Base64编码,属于冗余操作,增加了解密复杂度。
修复后的完整代码
密钥与IV配置(必须持久化存储)
const crypto = require("crypto"); // 生成一次后保存到环境变量或安全配置文件,禁止每次重启生成 const secretKey = process.env.SECRET_KEY || crypto.randomBytes(32).toString("hex"); const iv = process.env.IV || crypto.randomBytes(16).toString("hex"); // 生成符合要求的16字节IV // 统一转换为Buffer,避免加密解密时重复处理 const keyBuffer = Buffer.from(secretKey, "hex"); const ivBuffer = Buffer.from(iv, "hex");
修复后的加密函数
function encryptData(data) { console.log("Encrypted called"); const cipher = crypto.createCipheriv("aes-256-cbc", keyBuffer, ivBuffer); // 单次Base64编码即可,去掉冗余的二次编码 return cipher.update(data, "utf8", "base64") + cipher.final("base64"); }
修复后的解密函数
function decryptData(encryptedData) { try { // 如果之前已经存储了双重Base64编码的数据,保留下面两行;否则删除 // const buff = Buffer.from(encryptedData, "base64"); // encryptedData = buff.toString("utf-8"); const decipher = crypto.createDecipheriv("aes-256-cbc", keyBuffer, ivBuffer); return decipher.update(encryptedData, "base64", "utf8") + decipher.final("utf8"); } catch (error) { console.log("Error decrypting data:", error); // 对未加密数据直接返回原内容,避免报错 return encryptedData; } }
数据接收接口(简化密钥传递)
app.get("/recieve", async (req, res) => { try { const data = await UploadData.find(); const decryptedData = data.map((item) => { return { ...item.toObject(), fullname: decryptData(item.fullname), catName: decryptData(item.catName), email: decryptData(item.email), contact: decryptData(item.contact), location: decryptData(item.location), }; }); res.json(decryptedData); } catch (error) { console.error("Error fetching data:", error); res.status(500).json({ error: "Internal server error" }); } });
关键注意事项
- 务必持久化密钥和IV:将生成的
secretKey和iv存入环境变量(如.env文件)或加密配置存储,服务器重启时必须使用相同的密钥才能解密旧数据。 - 兼容未加密数据:解密函数中添加异常捕获后的返回逻辑,对未加密的原始数据直接返回,避免因非加密数据触发解密错误。
内容的提问来源于stack exchange,提问作者Shazib Mahmood
相关产品推荐
相关产品推荐

