2017-07-03 97 views
0

我无法弄清楚如何使用Node JS SDK中的KMS加密将文件上传到AWS S3。我不断收到403:访问被拒绝错误。我可以使用KMS从AWS S3获取文件。如何在节点js SDK中使用AWS KMS加密

我重用大部分代码从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参数是否应该采用某种独特的格式?我只是简单地传递我的钥匙。

+0

你从哪里读取文件?检查你是否有权首先从源文件读取文件。然后可能检查您正在使用的角色是否具有S3存储桶的更新写入权限。 – Ali

回答

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); 
     } 
    }); 
} 
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); 
});