Node.js中密钥错误导致bad decrypt异常的处理方案问询
处理AES解密的"bad decrypt"错误并返回false
你的代码存在几个关键问题,导致错误无法被正确捕获,最终引发程序崩溃:
- 同一个Promise内同时调用
rej(err)和res(false),Promise状态一旦确定就无法更改,重复调用会触发异常 - 解密出错时未清理临时文件,会产生垃圾文件残留
- 重新创建的读取流没有正确处理错误传播
- async回调中的错误未被捕获,可能导致未处理的Promise拒绝
解决方案步骤
- 移除所有
rej(err)调用,统一用res(false)返回失败状态,同时在错误发生时清理临时文件 - 用try-catch包裹
close事件内的异步代码,捕获密钥派生等步骤的错误 - 为所有流(reader、decipher、writer)添加统一的错误处理器,出错时销毁流并清理临时文件
- 确保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
相关产品推荐
相关产品推荐

