You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 11:43:20