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

使用AWS KMS加密解密S3文件时的匹配问题求助

问题分析与解决方案

核心问题定位

从日志和代码可以看出,解密后文件不匹配、长度异常的原因集中在加密/解密流程中数据格式转换的逻辑错误:

  1. 加密结果的错误转换:encryptFile中把用;连接的加密chunk hex字符串通过Buffer.from(encryptedData, 'hex')转换是完全错误的——;不是十六进制字符,会导致Buffer解析时直接截断后续数据,最终加密后的Buffer仅包含第一个chunk的内容。
  2. 解密时分割符识别失败:由于加密时的错误转换,解密阶段将Buffer转回hex字符串后,原分割符;已经变成十六进制的3b,无法通过buffer.includes(';')识别,导致仅能解密第一个chunk。
  3. 解密chunk的冗余转换:decryptChunk接收的参数已经是加密后的hex字符串,但函数内部错误地将其当作二进制Buffer再转hex,导致数据解析异常。

修复代码

1. 修复encryptFile的返回值

将;连接的hex字符串以utf8编码转Buffer,而非hex解析:

export const encryptFile = async (keyId, fileReadStream) => {
  if (!fileReadStream) throw new Error('Value to encrypt blank');
  if (!keyId || !keyId.length) {
    return fileReadStream;
  }
  let encryptBuffer = '';
  const encryptedChunks = [];
  for await (const chunk of fileReadStream) {
    try {
      const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
      encryptBuffer += chunkBuffer.toString('hex');

      while (encryptBuffer.length >= maxChunkSize * 2) {
        const encryptedChunk = encryptBuffer.slice(0, maxChunkSize * 2);
        encryptBuffer = encryptBuffer.slice(maxChunkSize * 2);

        const encryptedData = await encryptChunk(keyId, Buffer.from(encryptedChunk, 'hex'), 'enc0-chnk-fil');
        encryptedChunks.push(encryptedData);
      }
    } catch (error) {
      console.error('THIS IS THE CATCH ERROR (ENCFILE):', error);
      throw error;
    }
  }
  if (encryptBuffer.length > 0) {
    const encryptedData = await encryptChunk(keyId, Buffer.from(encryptBuffer, 'hex'), 'enc1-chnk-fil');
    encryptedChunks.push(encryptedData);
  }
  const encryptedData = encryptedChunks.join(';');
  // 修改:用utf8编码转换Buffer,保留分割符
  return Buffer.from(encryptedData, 'utf8');
};

2. 修复decryptFile的分割逻辑

解密时先将Buffer转回utf8字符串,再按;分割:

export const decryptFile = async (keyId, encryptedStream) => {
  if (!encryptedStream) throw new Error('Encrypted value blank');
  if (!keyId || !keyId.length) {
    return encryptedStream;
  }
  const decryptedChunks = [];
  let buffer = '';
  for await (const encryptedChunk of encryptedStream) {
    // 修改:转成utf8字符串,保留原分割符
    buffer += encryptedChunk.toString('utf8');
    while (buffer.includes(';')) {
      const delimiterIndex = buffer.indexOf(';');
      const encryptedChunkHex = buffer.slice(0, delimiterIndex);
      buffer = buffer.slice(delimiterIndex + 1);

      // 直接传入hex字符串给decryptChunk
      decryptedChunks.push(
        await decryptChunk(keyId, encryptedChunkHex)
      );
    }
  }
  if (buffer.length > 0) {
    decryptedChunks.push(
      await decryptChunk(keyId, buffer)
    );
  }
  return Buffer.concat(decryptedChunks);
};

3. 修复decryptChunk的参数处理

直接接收hex字符串并转换为CiphertextBlob:

export const decryptChunk = (keyId, encryptedHex) => (
  new Promise((resolve, reject) => {
    try {
      const cipherBlob = Buffer.from(encryptedHex, 'hex');
      if (!cipherBlob || !cipherBlob.length) {
        console.error('THERE WAS AN ERROR DECRYPTING CHUNK');
        return resolve(cipherBlob);
      }

      kms.decrypt({
        CiphertextBlob: cipherBlob,
        KeyId: keyId,
      }, (error, result) => {
        if (error) {
          return reject(error);
        }
        return resolve(Buffer.from(result.Plaintext));
      });
    } catch (error) {
      console.error('THIS IS THE CATCH ERROR: ', error);
      reject(new Error(error.message));
    }
  })
);

4. 测试函数冗余优化

downloadFromS3已返回Buffer,无需重复转换:

// 替换原代码
const newReadableStream = Readable.from([file]);

疑问解答

  1. 步骤与配置错误:核心错误是加密后的数据格式转换逻辑出错,导致加密数据丢失、解密时无法正确分割chunk,上述修复已解决所有配置问题。
  2. 文件类型兼容性:修复后的函数可以处理所有二进制文件(包括.zip、.docx、.pdf等),因为整个流程基于二进制Buffer处理,没有针对特定文件类型的限制,只要原文件能正确转换为Buffer,就能正常完成加密解密。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:34:52