AWS SDK v3 Node.js 18调用KMS解密S3文件遇byteLength未定义错误
问题原因
- 对SSE-KMS机制的误解:你用
--sse aws:kms上传的对象属于服务器端加密,下载时AWS S3会自动通过对应的KMS密钥解密,返回明文内容,根本不需要手动调用KMS的Decrypt接口。 - 代码的直接错误:AWS SDK v3中
GetObjectCommand返回的Body是ReadableStream对象,而KMS的DecryptCommand要求CiphertextBlob必须是Buffer或Uint8Array类型,直接传递Stream会触发序列化错误,也就是你看到的Cannot read properties of undefined (reading 'byteLength')。
解决方案
方案一:正确处理SSE-KMS对象下载(推荐)
既然SSE-KMS对象下载时自动解密,只需读取S3返回的流并转换成可处理的格式即可:
const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3"); const fs = require('fs'); const { pipeline } = require('stream/promises'); const s3Client = new S3Client({ region: 'ap-south-1' }); const s3Bucket = 'lambda-config'; const s3ObjectKey = 'config.json'; const outputFilePath = 'decrypted.txt'; async function fetchAndSaveFile() { const getObjectParams = { Bucket: s3Bucket, Key: s3ObjectKey }; try { const { Body } = await s3Client.send(new GetObjectCommand(getObjectParams)); // 直接将流写入文件 await pipeline(Body, fs.createWriteStream(outputFilePath)); console.log('文件已保存到', outputFilePath); } catch (error) { console.error('操作失败:', error); } } fetchAndSaveFile();
如果需要先将流转换成Buffer处理:
async function fetchFileFromS3(bucket, objectKey) { const getObjectParams = { Bucket: bucket, Key: objectKey }; try { const { Body } = await s3Client.send(new GetObjectCommand(getObjectParams)); // 将ReadableStream转换为Buffer const chunks = []; for await (const chunk of Body) { chunks.push(chunk); } return Buffer.concat(chunks); } catch (error) { console.error('从S3获取文件失败:', error); throw error; } } // 使用示例 fetchFileFromS3(s3Bucket, s3ObjectKey) .then(buffer => { fs.writeFileSync(outputFilePath, buffer); console.log('文件已保存'); }) .catch(err => console.error(err));
方案二:仅适用于客户端加密场景的手动解密
如果你是客户端加密后上传的对象(而非SSE-KMS),需要先把S3的Body转换成Buffer,再传给KMS:
const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3"); const { KMSClient, DecryptCommand } = require("@aws-sdk/client-kms"); const fs = require('fs'); const s3Client = new S3Client({ region: 'ap-south-1' }); const kmsClient = new KMSClient({ region: 'ap-south-1' }); const s3Bucket = 'lambda-config'; const s3ObjectKey = 'config.json'; const keyId = 'my_kms_key_id'; async function fetchFileFromS3(bucket, objectKey) { const getObjectParams = { Bucket: bucket, Key: objectKey }; try { const { Body } = await s3Client.send(new GetObjectCommand(getObjectParams)); // 将Stream转换为Buffer const chunks = []; for await (const chunk of Body) { chunks.push(chunk); } return Buffer.concat(chunks); } catch (error) { console.error('从S3获取文件失败:', error); throw error; } } async function decryptData(encryptedData) { const decryptParams = { CiphertextBlob: encryptedData, KeyId: keyId // 密钥上下文明确时可省略 }; try { const { Plaintext } = await kmsClient.send(new DecryptCommand(decryptParams)); return Plaintext; } catch (error) { console.error('解密失败:', error); throw error; } } const outputFilePath = 'decrypted.txt'; fetchFileFromS3(s3Bucket, s3ObjectKey) .then(async (fileData) => { const decryptedData = await decryptData(fileData); fs.writeFileSync(outputFilePath, decryptedData); console.log('解密后的数据已写入', outputFilePath); }) .catch(error => { console.error('发生错误:', error); });
关键说明
- SSE-KMS模式下,只要你的IAM角色拥有
s3:GetObject和kms:Decrypt(对应目标KMS密钥)权限,下载时S3会自动返回明文,无需额外操作。 - AWS SDK v3与v2的核心差异之一:S3的
GetObject返回的Body从v2的Buffer变成了v3的ReadableStream,必须先转换为Buffer才能作为二进制数据传递给其他服务。
内容的提问来源于stack exchange,提问作者DhirajAswani
相关产品推荐
相关产品推荐

