如何在节点js SDK中使用AWS KMS加密

时间:2017-07-03 23:59:29

标签: node.js amazon-web-services amazon-s3 aws-sdk

我无法弄清楚如何使用Node JS SDK中的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);
}

这里是否有我遗漏的东西,需要设置一个额外的元数据标签?

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

2 个答案:

答案 0 :(得分:1)

虽然关于检查权限的另一个答案无疑是正确的。我很难找到一个如何使用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);
        }
    });
}

答案 1 :(得分:0)

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

我回去使用aws-sdk节点模块,并从node-s3-encryption-client模块中获取了所有代码。

为了使用KMS加密将文件成功上传到Amazon S3,我需要做的就是在将params对象传递给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);
});