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

Node.js中密钥错误导致bad decrypt异常的处理方案问询

处理AES解密的"bad decrypt"错误并返回false

你的代码存在几个关键问题,导致错误无法被正确捕获,最终引发程序崩溃:

  • 同一个Promise内同时调用rej(err)和res(false),Promise状态一旦确定就无法更改,重复调用会触发异常
  • 解密出错时未清理临时文件,会产生垃圾文件残留
  • 重新创建的读取流没有正确处理错误传播
  • async回调中的错误未被捕获,可能导致未处理的Promise拒绝

解决方案步骤

  1. 移除所有rej(err)调用,统一用res(false)返回失败状态,同时在错误发生时清理临时文件
  2. 用try-catch包裹close事件内的异步代码,捕获密钥派生等步骤的错误
  3. 为所有流(reader、decipher、writer)添加统一的错误处理器,出错时销毁流并清理临时文件
  4. 确保Promise只被resolve一次,避免状态冲突

修改后的完整代码

import fs from 'fs';
import crypto from 'crypto';
import { promisify } from 'util';

const deleteFile = promisify(fs.unlink);
const renameFile = promisify(fs.rename);
const SALT_LENGTH = 16; // 按你的实际盐长度调整
const IV_LENGTH = 16; // AES-CBC标准IV长度

async function deriveKey(password: string, salt: string): Promise<Buffer> {
  // 替换为你的实际密钥派生逻辑,示例用pbkdf2
  return crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
}

function decrypt(path: string, password: string): Promise<boolean> {
  let reader = fs.createReadStream(path);
  let salt: Buffer | null = null;
  let iv: Buffer | null = null;

  return new Promise(async (res) => {
    // 处理第一个读取流的错误
    reader.on('error', () => res(false));

    reader.on('readable', () => {
      if (!salt) salt = reader.read(SALT_LENGTH);
      if (salt && !iv) iv = reader.read(IV_LENGTH);

      if (salt && iv) reader.close();
    });

    reader.on('close', async () => {
      const tmpPath = path + '.tmp';
      let innerReader: fs.ReadStream | null = null;
      let decipher: crypto.DecipherIV | null = null;
      let writer: fs.WriteStream | null = null;

      // 统一错误处理逻辑
      const handleError = async () => {
        // 销毁所有流释放资源
        innerReader?.destroy();
        decipher?.destroy();
        writer?.destroy();
        // 清理临时文件
        try { await deleteFile(tmpPath); } catch {}
        res(false);
      };

      try {
        innerReader = fs.createReadStream(path, { start: SALT_LENGTH + IV_LENGTH });
        const key = await deriveKey(password, salt!.toString('ascii'));
        decipher = crypto.createDecipheriv('aes-256-cbc', key, iv!);
        writer = fs.createWriteStream(tmpPath);

        // 绑定所有流的错误处理器
        innerReader.on('error', handleError);
        decipher.on('error', handleError);
        writer.on('error', handleError);

        // 建立管道流
        innerReader.pipe(decipher).pipe(writer);

        writer.on('finish', async () => {
          try {
            await deleteFile(path);
            await renameFile(tmpPath, path);
            res(true);
          } catch {
            // 文件操作失败时清理临时文件
            try { await deleteFile(tmpPath); } catch {}
            res(false);
          }
        });
      } catch {
        // 捕获密钥派生等步骤的错误
        try { await deleteFile(tmpPath); } catch {}
        res(false);
      }
    });
  });
}

// 示例调用
encrypt("test.txt", "password")
  .then(() => decrypt("test.txt", "passworddd"))
  .then(console.log);

关键修改说明

  • 新增handleError统一处理所有流错误,确保资源释放和临时文件清理
  • Promise仅通过res()返回结果,避免状态冲突
  • 用try-catch包裹所有可能抛出错误的异步操作,覆盖所有异常场景
  • 所有流的错误都被捕获,不会触发未处理异常导致程序崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:20:15