Managing Encryption Keys With AWS KMS In Node.js – Hacker Noon

Security is very important when developing applications. How do you encrypt data and manage encryption keys in your application? Successful key management is critical to the security of a cryptosystem. This is where KMS’s come into play. Let’s first see what a KMS really is.

Key Management System (KMS)

According to Wikipedia,

A key management system (KMS), also known as a crytographic key management system (CKMS), is an integrated approach for generating, distributing and managing cryptographic keys for devices and applications. Compared to the term key management, a KMS is tailored to specific use-cases such as secure software update or machine-to-machine communication. In an holistic approach, it covers all aspects of security — from the secure generation of keys over the secure exchange of keys up to secure key handling and storage on the client. Thus, a KMS includes the backend functionality for key generation, distribution, and replacement as well as the client functionality for injecting keys, storing and managing keys on devices. With the Internet of Things, KMS becomes a crucial part for the security of connected devices.

A KMS makes your life easy when dealing with key management and encryption.

AWS KMS

Now we know what a key management system is. Let’s talk about AWS KMS briefly.

AWS KMS is a service by AWS that makes it easy for you to manage your encryption keys. It uses Hardware Security Modules (HSMs) in the backend. AWS KMS is integrated with other AWS services. It is also low in cost. AWS KMS provides access control to your keys so that you can determine who can access the keys when can the keys be accessed and a lot of other options.

AWS KMS uses the Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM), known as AES-GCM. AWS KMS uses this algorithm with 256-bit secret keys.

AWS KMS pricing can be viewed here. Free tier includes 20,000 requests/month.

Using AWS KMS With Node.js

In this section I am going to share how to use AWS KMS within your Node.js application. I will cover the following topics here,

  • How to create a CMK (Customer Master Key).
  • How to attach an IAM user to CMK with an IP restricted policy.
  • How to encrypt data using CMK.
  • How to decrypt data using CMK.
  • Envelope encryption using AWS KMS.
  • Encryption context (Intro)

With that being said I want to first state that I am not a crypto expert!.

How to create a CMK (Customer Master Key)

The primary resources in AWS KMS are customer master keys (CMKs). CMKs are either customer managed or AWS managed. Let’s take a look at how to create a customer master key in AWS.

Login to AWS console and go to services. Then select “IAM”. There on the left side you can see a section called “Encryption Keys”. Navigate to that section. Then select “Create Key” on top left.

Enter an alias for the key and a description. In the advanced options select KMS. Here you can import your own external key if you want. I am not going to cover how to import an external key here (It is also pretty straight forward).

Click next. Add tags (optional) if you require. Go to next step again. If you have an IAM user already created (If not skip selecting a user. We will create a user later) and if you want to allow that user to access this key select the user from the list and click next. Finally finish the process. Your key will be created now.

How to attach an IAM user to CMK with an IP restricted policy

Now if you don’t have a previously created IAM user, follow these steps to create one.

Go to “IAM” (as above). Select “Users” on the left side. Select “Add user” top left corner. Fill the required details. An example is given below.

Select next. Select the option “Attach existing policies directly”. Then select “Create policy”. You can visually edit the policy or edit the json of the policy. I am going to edit the json here. Select “json” tab. I will be using the below simple policy which will only allow access to the CMK from a one selected IP. You can select an IP range or you can customize the policy in any way you want.

policy

After that follow the instructions to save the created policy. Then finish creating the user. At the last page new user’s Access key ID and Secret access key will be displayed. Download those information. You won’t be able to view it afterwards (But you can create new credentials again).

Now go to Encryption keys and select the key that you just created. Go to “Key Users” section and select “Add”. Select the user you have created. Then that user will be added to the allowed list for the CMK you created.

Key Users

How to encrypt data using CMK

Enough setting up things, let’s see how to use AWS KMS!

I am using AWS SDK for Node.js here. Let’s install AWS SDK package from npm.

npm install aws-sdk

Following function encrypts a given buffer and outputs the cipher text blob.

function encrypt(buffer) {
const kms = new aws.KMS({
accessKeyId: 'AKCVBTRNOSMLTIA7RPQQ', //credentials for your IAM user
secretAccessKey: 'lJQtdIfH/Cup9AyaaHV8h2NnR/eKFIsZea5Vn0k', //credentials for your IAM user
region: 'ap-southeast-1'
});
return new Promise((resolve, reject) => {
const params = {
KeyId: '965d2884-b2cd-4d79-8773-6b1f57133300', // The identifier of the CMK to use for encryption. 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.
Plaintext: buffer// The data to encrypt.
};
kms.encrypt(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.CiphertextBlob);
}
});
});
}

How to decrypt data using CMK

Following function decrypts a cipher text blob that was encrypted before and returns the plain text buffer.

function decrypt(buffer) {
const kms = new aws.KMS({
accessKeyId: 'AKCVBTRNOSMLTIB7ROQQ',
secretAccessKey: 'lJQtdIfH/Cup9AyabHV9h2NnR/eKFIsZea2Vn0k',
region: 'ap-southeast-1'
});
return new Promise((resolve, reject) => {
const params = {
CiphertextBlob: buffer// The data to dencrypt.
};
kms.decrypt(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.Plaintext);
}
});
});
}

Following code encrypts the text “abc” then decrypts it and prints the value to the console

encrypt(new Buffer('abc','utf-8')).then(decrypt).then(plaintext => {
console.log(plaintext.toString('utf-8'));
});

Envelope encryption using AWS KMS

AWS KMS has a size limit on directly encrypting data using the CMK. You can only encrypt up to 4 kilobytes of data per request. If you want to encrypt data of bigger size, for an example a video, you need to use envelope encryption.

In envelope encryption we generate a data key by using our CMK at KMS. When generating the data key, AWS sends us both the plaintext key and the encrypted key (using our CMK). Then we use the plaintext data key generated to encrypt our data. After encrypting the data we destroy the plaintext key and keep the encrypted key with us. When we want to decrypt the data we send the encrypted key to AWS KMS and get back the plaintext key and continue with decryption.

First we need to generate a data key from AWS KMS. Following diagram depicts that procedure.

http://docs.aws.amazon.com

Following function generates a data key from AWS KMS.

function generateDataKey() {
const kms = new aws.KMS({
accessKeyId: 'AKCVBTRNOSMLTIB7ROQQ',
secretAccessKey: 'lJQtdIfH/Cup9AyabHV9h2NnR/eKFIsZea2Vn0k',
region: 'ap-southeast-1'
});
return new Promise((resolve, reject) => {
const params = {
KeyId: '965d2884-b2ab-4e78-8773-6b1f57133300', // 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.
KeySpec: 'AES_256'// Specifies the type of data key to return.
};
kms.generateDataKey(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}

“data” has both encrypted data key and plain text data key as buffers.

Next step is encrypting data with plain text data key.

http://docs.aws.amazon.com

Following is an example code which uses AES-CBC to encrypt data using the data key. This uses crypto package. Here I have used a fixed iv (initialization vector) for the simplicity.

function encryptAES(key, buffer) {
const algorithm = 'AES-256-CBC';

const iv = new Buffer('00000000000000000000000000000000', 'hex');

encryptor = crypto.createCipheriv(algorithm, key, iv);
encryptor.write(strBuffer);
encryptor.end();

return encryptor.read();
}

Let’s see how to decrypt the encrypted data. First of all we need to decrypt our ecrypted key using the CMK. Following diagram illustrates that. The code snippet I have posted earlier can be used to do this (just the same as regular data decryption).

http://docs.aws.amazon.com

The last step is decrypting the data. Following diagram illustrates that.

http://docs.aws.amazon.com

A sample code to decrypt the data we encrypted above using AES-CBC is shown below.

function decryptAES(key, buffer) {
const algorithm = 'AES-256-CBC';

const iv = new Buffer('00000000000000000000000000000000', 'hex');

encryptor = crypto.createDecipheriv(algorithm, key, iv);
encryptor.write(buffer);
encryptor.end();

return encryptor.read();
}

Following is a code snippet to encrypt and decrypt a string using envelope encryption.

generateDataKey()
.then(data => {
const cipherTextBlob = encryptAES(data.Plaintext, new Buffer('abc', 'utf-8'));
for (let i = 0; i < data.Plaintext.length; i++) {
data.Plaintext[i] = null;
}
decrypt(data.CiphertextBlob)
.then(key => {
const dataBuffer = decryptAES(key, cipherTextBlob);
console.log(dataBuffer.toString('utf-8'));
});
});

Encryption context

Other than all of these AWS KMS provides support for encryption context to further enhance the security. There are some security issues that can be mitigated by using an encryption context. To learn more about encryption context and how to use it, please head over here.

OK we are at the end. This has been a pretty lengthy article. But I hope that you have learned something from this. Now its your time to check AWS KMS out!!!

read original article here