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

Node Crypto解密文件报错:Unsupported state or unable to authenticate data

Node.js AES-GCM 解密报错 "Unsupported state or unable to authenticate data" 解决方法

问题根源

你的代码存在两个关键问题导致解密失败:

  • Buffer拼接方式错误:使用+拼接Buffer会触发隐式字符串转换,破坏二进制数据结构,导致AuthTag和加密内容无法正确解析。
  • 异步执行顺序错误:file.encrypt()和file.decrypt()连续调用时,加密操作的异步fs.readFile还未完成,解密就已读取原文件内容,导致解密逻辑失效。

修正后的代码

加密解密核心模块

const crypto = require('crypto');
const fs = require('fs');

const app = {
    encrypt(data, password) {
        const salt = crypto.randomBytes(16);
        const key = crypto.scryptSync(password, salt, 32, { N: 16384 });
        const cipher = crypto.createCipheriv('aes-256-gcm', key, salt);
        const encryptedData = Buffer.concat([cipher.update(data), cipher.final()]);
        const authTag = cipher.getAuthTag();

        // 用Buffer.concat正确合并二进制数据
        return Buffer.concat([salt, authTag, encryptedData]);
    },
    decrypt(data, password) {
        const salt = data.slice(0, 16);
        const authTag = data.slice(16, 32);
        const encData = data.slice(32);
        const key = crypto.scryptSync(password, salt, 32, { N: 16384 });
        const decipher = crypto.createDecipheriv('aes-256-gcm', key, salt);
        decipher.setAuthTag(authTag);

        const plainText = Buffer.concat([decipher.update(encData), decipher.final()]);
        return plainText;
    }
}

文件操作模块(修正异步顺序)

const fileLoc = './sample.txt';
const password = 'password';

const file = {
    encrypt() {
        return new Promise((resolve, reject) => {
            fs.readFile(fileLoc, (err, data) => {
                if (err) {
                    console.log('File not found.');
                    reject(err);
                    return;
                }
                const cipherText = app.encrypt(data, password);
                fs.writeFileSync(fileLoc, cipherText);
                console.log('加密完成');
                resolve();
            });
        });
    },
    decrypt() {
        return new Promise((resolve, reject) => {
            fs.readFile(fileLoc, (err, data) => {
                if (err) {
                    console.log('File not found.');
                    reject(err);
                    return;
                }
                try {
                    const plainText = app.decrypt(data, password);
                    fs.writeFileSync(fileLoc, plainText);
                    console.log('解密完成');
                    resolve();
                } catch (decryptErr) {
                    console.log('解密失败:', decryptErr.message);
                    reject(decryptErr);
                }
            });
        });
    }
}

// 保证加密完成后再执行解密
file.encrypt().then(() => {
    file.decrypt();
}).catch(err => {
    console.error(err);
});

额外说明

  • AES-GCM模式下,AuthTag的长度默认是16字节,你的切片逻辑是正确的,核心问题还是Buffer拼接和异步顺序。
  • 用Promise封装异步操作可以更清晰地控制执行顺序,避免回调嵌套。
  • 处理二进制文件时,必须始终用Buffer操作,避免任何隐式的字符串转换。

内容的提问来源于stack exchange,提问作者Akshay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:25:19