如何在节点 js 开发工具包中使用 AWS KMS 加密



我无法弄清楚如何使用 Node JS 开发工具包的 KMS 加密将文件上传到 AWS S3。我不断收到 403:拒绝访问错误。我能够使用 KMS 从 AWS S3 获取文件。

我正在重用 https://github.com/gilt/node-s3-encryption-client 中的大部分代码

主类

var fs = require('fs'),
AWS = require('aws-sdk'),
crypt = require("./crypt"),
kms,
s3;
const metadataCipherAlgorithm = 'cipher-algorithm',
metadataDecryptedEncoding = 'decrypted-encoding'
metadataKmsKeyName = 'x-amz-key';

/**
* Constructor - Initializes S3 sdk connection
*/
function S3FileStreamer(key, secret, region) {
if (region) {
AWS.config.region = region;
}
//set credentials if passed in
if (key && secret) {
AWS.config.update({accessKeyId: key, secretAccessKey: secret})
}
s3 = new AWS.S3({signatureVersion: "v4"});
kms = new AWS.KMS({apiVersion: '2014-11-01'});
}
S3FileStreamer.prototype.uploadFile = function(bucket, key, kmsKey, filename, onComplete) {
var params = {
Bucket: bucket,
Key: key,
Body: fs.readFileSync(filename),
ContentType: getMimeType(filename)
};
params.KmsParams = {
KeyId: kmsKey,
KeySpec: 'AES_256'
}
kmsUpload(params, function(err, data) {
if (err) onComplete(err, null);
else {
onComplete(err, data);
}
});
};
function kmsUpload(params, callback) {
var kmsParams = params.KmsParams
if (kmsParams && kmsParams.KeyId) {
kms.generateDataKey(kmsParams, function(err, kmsData) {
if (err) {
callback(err, null);
} else {
var helper = new crypt.Helper(kmsData.Plaintext.toString('base64'), {algorithm: params.CipherAlgorithm, decryptedEncoding: params.DecryptedEncoding});
params.Body = helper.encrypt(params.Body);
params.Metadata = params.Metadata || {};
params.Metadata[metadataKmsKeyName] = kmsData.CiphertextBlob.toString('base64');
if (params.CipherAlgorithm) params.Metadata[metadataCipherAlgorithm] = params.CipherAlgorithm;
if (params.DecryptedEncoding) params.Metadata[metadataDecryptedEncoding] = params.DecryptedEncoding;
putObject(params, callback);
}
})
} else {
putObject(params, callback);
}
}
function putObject(params, callback) {
delete params.KmsParams;
delete params.CipherAlgorithm;
delete params.DecryptedEncoding;
s3.putObject(params, callback);
}

地穴类

var crypto = require('crypto');
/*
options:
algorithm: Anything from crypto.getCiphers()
decryptedEncoding: 'utf8', 'ascii', or 'binary'
outputEncoding: 'binary', 'base64', or 'hex'
*/
exports.Helper = function(password, options) {
this.password = password;
options = options || {};
this.algorithm = options.algorithm || 'aes-256-cbc';
this.decryptedEncoding = options.decryptedEncoding || 'utf8';
this.encryptedEncoding = options.encryptedEncoding || 'base64';
}
exports.Helper.prototype.encrypt = function(unencrypted) {
var cipher = crypto.createCipher(this.algorithm, this.password);
return cipher.update(unencrypted, this.decryptedEncoding, this.encryptedEncoding) + cipher.final(this.encryptedEncoding);
}
exports.Helper.prototype.decrypt = function(encrypted) {
var decipher = crypto.createDecipher(this.algorithm, this.password);
return decipher.update(encrypted, this.encryptedEncoding, this.decryptedEncoding) + decipher.final(this.decryptedEncoding);
}

我在这里缺少什么,需要设置额外的元数据标签吗?

传递给 kmsgenerateDataKey方法的 keyId 参数是否应该采用某种唯一格式?我只是简单地传递我的钥匙。

而关于检查权限的另一个答案无疑是正确的。 我很难找到如何使用AES GCM加密算法解码S3对象的好例子。 我设法让这段代码基于 aws ruby sdk 工作(因为我发现 node-s3-encryption-client 有点旧(。

/**
* Decrypt s3 file data
* @param  {object}   objectData result of s3 get call
* @param  {Function} callback   function(err, data) returns error or decrypted data
*/
function decrypt(objectData, callback) {
var metadata = objectData.Metadata || {};
var kmsKeyBase64 = metadata['x-amz-key-v2'];
var iv = metadata['x-amz-iv'];
var tagLen = (metadata['x-amz-tag-len'] || 0)/8;
var algo = metadata['x-amz-cek-alg'];
var encryptionContext = JSON.parse(metadata['x-amz-matdesc']);
switch (algo) {
case 'AES/GCM/NoPadding':
algo = 'aes-256-gcm';
break;
case 'AES/CBC/PKCS5Padding':
algo = 'aes-256-cbc';
break;
default:
callback(new Error('Unsupported algorithm: ' + algo), null);
return;
}
if (typeof (kmsKeyBase64) === 'undefined') {
callback(new Error('Missing key in metadata'), null);
return;
}
var kmsKeyBuffer = new Buffer(kmsKeyBase64, 'base64');
kms.decrypt({
CiphertextBlob: kmsKeyBuffer,
EncryptionContext: encryptionContext
}, function(err, kmsData) {
if (err) {
callback(err, null);
} else {
var decipher = crypto.createDecipheriv(algo,
kmsData.Plaintext,
new Buffer(iv, 'base64'));
if (tagLen !== 0) {
// the tag is appended to the data buffer
var tag = objectData.Body.slice(-tagLen);
decipher.setAuthTag(tag);
}
var data = objectData.Body.slice(0,-tagLen);
var dec = decipher.update(data, 'binary', 'utf8');
dec += decipher.final('utf8');
console.log("Decoded:", dec);
callback(null, dec);
}
});
}

感谢您的所有帮助。我想出了我的问题的解决方案。

我回到了仅使用 aws-sdk 节点模块的使用,并取出了从 node-s3-encryption-client 模块获得的所有代码。

要使用 KMS 加密将文件成功上传到 Amazon S3,我需要做的就是在将参数对象传递给 putObject 方法之前添加两个参数。这些参数ServerSideEncryptionSSEKMSKeyId,如下所示。它现在可以工作了!

var params = {
Bucket: bucket,
Key: key,
Body: fs.readFileSync(filename),
ContentType: getMimeType(filename),
ServerSideEncryption: 'aws:kms',
SSEKMSKeyId: kmsKey
};
s3.putObject(params, function(err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
});

最新更新