使用AWS KMS加密解密S3文件时的匹配问题求助
问题分析与解决方案
核心问题定位
从日志和代码可以看出,解密后文件不匹配、长度异常的原因集中在加密/解密流程中数据格式转换的逻辑错误:
- 加密结果的错误转换:
encryptFile中把用;连接的加密chunk hex字符串通过Buffer.from(encryptedData, 'hex')转换是完全错误的——;不是十六进制字符,会导致Buffer解析时直接截断后续数据,最终加密后的Buffer仅包含第一个chunk的内容。 - 解密时分割符识别失败:由于加密时的错误转换,解密阶段将Buffer转回hex字符串后,原分割符
;已经变成十六进制的3b,无法通过buffer.includes(';')识别,导致仅能解密第一个chunk。 - 解密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]);
疑问解答
- 步骤与配置错误:核心错误是加密后的数据格式转换逻辑出错,导致加密数据丢失、解密时无法正确分割chunk,上述修复已解决所有配置问题。
- 文件类型兼容性:修复后的函数可以处理所有二进制文件(包括.zip、.docx、.pdf等),因为整个流程基于二进制Buffer处理,没有针对特定文件类型的限制,只要原文件能正确转换为Buffer,就能正常完成加密解密。
内容的提问来源于stack exchange,提问作者user7160059
相关产品推荐
相关产品推荐

