AWS: Envelop Encryption & Decryption using AWS Key Management Service (Envelop encryption) - Node.JS Version

Recently I published a post on AWS KMS encryption using .net / .net core. Now I got a chance to work on AWS KMS encryption using Node.Js. Following is the Node.Js version of the KMS helper class given in the previous post.
I used the aws-sdk npm for this

encrypt.js

'use strict';
var AWS = require("aws-sdk"),
    crypto = require('crypto'),
    algorithm = 'AES-256-CBC';
var awsconfig = {
    "accessKey": "<Your AWS Access Key>",
    "secretAccessKey": "<Your AWS Secret Key>",
    "region": "<Your AWS region>",
    "cmkArn": "<Your KMS master Key arn >" // The identifier of the CMK to use to encrypt the data key. You can use the key ID or Amazon Resource Name (ARN) of the CMK, or the name or ARN of an alias that refers to the CMK.
}

// Creates the KMS client
function getKMSClient() {
    var credentials = new AWS.Credentials(awsconfig.accessKey, awsconfig.secretAccessKey);
    AWS.config.update({
        region: awsconfig.region,
        credentials: credentials
    });

    return new AWS.KMS();
}

// Generates the KMS data Key
function generateDataKey() {
    const kms = getKMSClient();
    return new Promise((resolve, reject) => {
        const params = {
            KeyId: awsconfig.cmkArn,
            KeySpec: 'AES_256'// Specifies the type of data key to return.
        };
        kms.generateDataKey(params, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

// Decrypt the KMS Data key
function decryptDataKey(CiphertextBlob) {
    const kms = getKMSClient();
    return new Promise((resolve, reject) => {
        kms.decrypt({ CiphertextBlob }, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

module.exports = {

    encrypt: function (text) {
        return new Promise((resolve, reject) => {
            generateDataKey().then(dataKey => {
                // Get a random IV
                const iv = crypto.randomBytes(16);
                // Copy the length of the CiphertextBlob as first byte and concatenate the CiphertextBlob to it
                var length = Buffer.from(['0x' + dataKey.CiphertextBlob.length.toString(16)]);
                var str = Buffer.concat([length, dataKey.CiphertextBlob])
                // Concatenate the iv to the buffer
                str = Buffer.concat([str, iv]);
                
                // Create aes encryptor with KMS plain key
                var encryptor = crypto.createCipheriv(algorithm, dataKey.Plaintext, iv);
                // Encrypt the data
                encryptor.write(text);
                encryptor.end();
                // Concatenate the encrypted to the buffer and return the base64 string
                str = Buffer.concat([str, encryptor.read()]);
                resolve(str.toString('base64'));
            }, error => {
                reject(error);
            });
        });

    },

    decrypt: function (text) {
        return new Promise((resolve, reject) => {
            try {
                // Convert the base64 string to buffer
                var buf = Buffer.from(text, 'base64');
                // Get the CiphertextBlob length stored in the firsty byte
                var length = parseInt(buf[0].toString());
                // Extract the CiphertextBlob, IV and 
                var cipherBlob = buf.slice(1, length+1);
                var iv = buf.slice(length + 1, length + 1 + 16);
                var encText = buf.slice(length + 1 + 16, buf.length);
                // Decrypt the CiphertextBlob and get the plaintext key
                decryptDataKey(cipherBlob).then(dataKey => {
                    // Create aes decryptor and decrypt the text
                    var decryptor = crypto.createDecipheriv(algorithm, dataKey.Plaintext, iv)
                    decryptor.write(encText);
                    decryptor.end();
                    resolve(decryptor.read().toString());
                }, error => {
                    reject(error);
                });
            }
            catch (err) {
                reject(error);
            }
        });
    }

}

routes.js

'use strict';
var express = require('express');
var kmsHelper = require('./encrypt');
var router = express.Router();

/* GET home page. */
router.get('/', function (req, res) {
    res.render('home.html');
});

router.post('/api/encrypt', function (req, res) {
    try {
        kmsHelper.encrypt(req.body.text).then(result => {
            res.send(result.toString());
        }, err => {
            res.statusCode = 404;
            res.send(err);
        });
    }
    catch (ex) {
        res.statusCode = 404;
        res.send(ex);
    }
});

router.post('/api/decrypt', function (req, res) {
    try {
        kmsHelper.decrypt(req.body.text).then(result => {
            res.send(result.toString());
        }, err => {
            res.statusCode = 404;
            res.send(err);
        });
    }
    catch (ex) {
        res.statusCode = 404;
        res.send(ex);
    }
});


module.exports = router;    

Happy Coding! 😊

Gopikrishna

    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment