I cannot figure out how to upload files into AWS S3 using KMS encryption from the Node JS SDK. I keep getting a 403: Access Denied error. I am able to get files from AWS S3 using KMS.
我无法弄清楚如何使用Node JS SDK中的KMS加密将文件上传到AWS S3。我一直收到403:拒绝访问错误。我可以使用KMS从AWS S3获取文件。
I am reusing most of the code from https://github.com/gilt/node-s3-encryption-client
我正在重用https://github.com/gilt/node-s3-encryption-client中的大部分代码
Main Classvar 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);
}
Crypt class
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);
}
Is there something I am missing here, an extra metadata tag that needs to be set?
我在这里缺少什么,需要设置额外的元数据标签吗?
Is the keyId parameter that is passed to the kms generateDataKey
method supposed to be in some sort of unique format? I am just simply passing in my key.
传递给kms generateDataKey方法的keyId参数是否应该采用某种独特的格式?我只是简单地传递我的钥匙。
1
While the other answer about checking permissions is undoubtedly correct. I had a hard time finding a good example of how to decode S3 objects using the AES GCM encryption algorithm. I managed to get this code to work based on the aws ruby sdk (as I found the node-s3-encryption-client a little old).
而关于检查权限的另一个答案无疑是正确的。我很难找到一个如何使用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);
}
});
}
0
Thanks for all the help. I figured out the solution to my question.
谢谢你的帮助。我想出了我的问题的解决方案。
I went back to using just using the aws-sdk node module and took out all the code I got from the node-s3-encryption-client module.
我回去使用aws-sdk节点模块,并从node-s3-encryption-client模块中取出了所有代码。
All I needed to do in order to successfully upload a file into Amazon S3 using KMS encryption was to add two parameters before passing my params object to the putObject method. These parameters were ServerSideEncryption
and SSEKMSKeyId
as shown below. It now works!
为了使用KMS加密将文件成功上传到Amazon S3,我需要做的就是在将params对象传递给putObject方法之前添加两个参数。这些参数是ServerSideEncryption和SSEKMSKeyId,如下所示。它现在有效!
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);
});