使用KMS解密来自S3的SES消息



我正在尝试解密存储在S3中的KMS加密的AWS SES消息。

一切都很好(从s3获取对象和元数据,用KMS解密密钥(,直到我尝试用解密的密钥解密内容:

import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
import { createDecipheriv } from 'crypto';
const s3 = new S3Client( {
// ...credentials
} );
const kms = new KMSClient( {
// ...credentials
} );
const getFromS3AndDecrypt = ( Bucket, Key ) => {
// GetObject command
const command = new GetObjectCommand( {
Bucket,
Key,
} );

// Get the object from S3
const { Metadata, Body } = await s3.send( command );
// Convert to Buffer
const body = await streamToBuffer( Body );
// Get KMS Metadata
var {
[ 'x-amz-key-v2' ]: kmsKeyBase64,
[ 'x-amz-iv' ]: iv,
[ 'x-amz-tag-len' ]: tagLenBits = 0,
[ 'x-amz-cek-alg' ]: algo,
[ 'x-amz-matdesc' ]: matdesc,
} = Metadata;
// Convert tagLenBits to Bytes
const tagLen = tagLenBits / 8;
// Get encryption context
const encryptionContext = JSON.parse( matdesc );
// Get algorithm
switch ( algo ) {
case 'AES/GCM/NoPadding':
algo = `aes-256-gcm`;
break;
case 'AES/CBC/PKCS5Padding':
algo = `aes-256-cbc`;
break;
default:
throw new Error( `Unsupported algorithm ${ algo }` );
}
// Convert kmsKey to Buffer
const kmsKeyBuffer = Buffer.from( kmsKeyBase64, 'base64' );
// DecryptCommand
const kmsCommand = new DecryptCommand( {
CiphertextBlob: kmsKeyBuffer,
EncryptionContext: encryptionContext,
} );
// Decrypt the key with KMS
const { Plaintext } = await kmsClient.send( kmsCommand );
// Create decipher with key and iv
const decipher = createDecipheriv( algo, Buffer.from( Plaintext ), Buffer.from( iv ), {
authTagLength: 16,
} );
// Body without authTag
const data = body.slice( 0, - tagLen );
if  ( tagLen !== 0 ) {

// authTag
const tag = body.slice( - tagLen );
// Set authTag
decipher.setAuthTag( tag );
}
// Decrypt data
var decrypted = decipher.update( data, 'binary', 'utf8' );

decrypted += decipher.final( 'utf8' );
return decrypted;
};
// Turn @aws-sdk/client-s3 stream response into buffer @see https://github.com/aws/aws-sdk-js-v3/issues/1877#issuecomment-755387549
const streamToBuffer = ( stream ) => new Promise( ( resolve, reject ) => {

const chunks = [];

stream.on( 'data', ( chunk ) => chunks.push( chunk ) );

stream.on( 'error', reject );

stream.on( 'end', () => resolve( Buffer.concat( chunks ) ) );

} );

当调用decipher.final时,我得到一个Unsupported state or unable to authenticate data错误。

这似乎是因为使用的authTag与加密数据时使用的不匹配,或者是因为输入和输出编码不匹配。

iv和Plaintextkey都已被base64编码,因此需要将编码传递给Buffer:

import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
import { createDecipheriv } from 'crypto';
const s3 = new S3Client( {
// ...credentials
} );
const kms = new KMSClient( {
// ...credentials
} );
const getFromS3AndDecrypt = ( Bucket, Key ) => {
const { Metadata, Body } = await s3.send( new GetObjectCommand( {
Bucket,
Key,
} ) );
const body = await streamToString( Body );
const { Plaintext } = await kms.send( new DecryptCommand( {
CiphertextBlob: Buffer.from( Metadata[ 'x-amz-key-v2' ], 'base64' ),
EncryptionContext: JSON.parse( Metadata[ 'x-amz-matdesc' ] ),
} ) );
const key = Buffer.from( Plaintext, 'base64' );
const iv = Buffer.from( Metadata[ 'x-amz-iv' ], 'base64' );
const tag = body.slice( -16 );
const data = body.slice( 0, -16 );
const decipher = createDecipheriv( 'aes-256-gcm', key, iv );
decipher.setAuthTag( tag );
var decrypted = decipher.update( data, 'binary', 'utf8' );
decrypted += decipher.final( 'utf8' );
return decrypted;
};
// Turn @aws-sdk/client-s3 stream response into buffer @see https://github.com/aws/aws-sdk-js-v3/issues/1877#issuecomment-755387549
const streamToString = ( stream ) => new Promise( ( resolve, reject ) => {

const chunks = [];

stream.on( 'data', ( chunk ) => chunks.push( chunk ) );

stream.on( 'error', reject );

stream.on( 'end', () => resolve( Buffer.concat( chunks ) ) );

} );

最新更新