Using AWS Key Management (KMS) to encrypt and decrypt in AWS Lambda (NodeJS)

AWS Key Management (KMS) is a fully managed service that makes it easy to create and control encryption keys on AWS which can then be utilised to encrypt and decrypt data in a safe manner. The service leverages Hardware Security Modules (HSM) under the hood which in return guarantees security and integrity of the generated keys.

You can enable AWS KMS to store your data-at-rest on many AWS storage solutions (like DynamoDB and EBS). However in our Lambda functions we would like to encrypt (and decrypt) certain values on runtime. So we needed some code to encrypt and decrypt. Took me some while to figure it out, so here it is.

Here is the function to encrypt any string against the AWS KMS Key. You can create a KMS Key through AWS IAM in the console or (much better) AWS CloudFormation.

const kmsClient = new AWS.KMS({region: 'eu-west-1'});

/**
 * Encrypt
 */
async function encryptString(text: string): Promise<string> {

    const paramsEncrypt = {
        KeyId: 'arn:aws:kms:eu-west-1:........',
        Plaintext: new Buffer(text)
    };

    const encryptResult = await kmsClient.encrypt(paramsEncrypt).promise();
    // The encrypted plaintext. When you use the HTTP API or the AWS CLI, the value is Base64-encoded. Otherwise, it is not encoded.
    if (Buffer.isBuffer(encryptResult.CiphertextBlob)) {
        return Buffer.from(encryptResult.CiphertextBlob).toString('base64');
    } else {
        throw new Error('Mayday Mayday');
    }
}

And we can use the result (an encrypted string) to store it in DynamoDB, Aurora or send it wherever we want. So in the end we would like to decrypt it as well. So …

/**
 * Decrypt
 */
async function decryptEncodedstring(encoded: string): Promise<string> {

    const paramsDecrypt: AWS.KMS.DecryptRequest = {
        CiphertextBlob: Buffer.from(encoded, 'base64')
    };

    const decryptResult = await kmsClient.decrypt(paramsDecrypt).promise();
    if (Buffer.isBuffer(decryptResult.Plaintext)) {
        return Buffer.from(decryptResult.Plaintext).toString();
    } else {
        throw new Error('We have a problem');
    }
}

Hope it helps!

References

  • Example code including test function available in my Github as well