EE-AWS_KMS.png
profile picture Aman Bisht
5 min read Apr 27, 2021

How to implement Envelope encryption using AWS KMS

Why is encryption necessary?

With the increased number of data breaches in recent times, data security and privacy have become the most important facet for businesses. Organizations today rely heavily on the power of encryption to make data transfer and storage as confidential and secure as possible.

For data in transit, TLS/SSL provides a secure session between a client and a server and prevents any man-in-the-middle attacks but encrypting data at rest remains much of a concern. While mobile applications could implement end-to-end encryption, a similar approach is not feasible for web applications. In such scenarios, the concept of envelope encryption comes to the rescue and provides a better alternative to secure the data.

What is envelope encryption?

Envelope encryption is the method of encrypting data with a data encryption key (DEK) and then encrypting the DEK with a fully manageable root key. Multiple cloud platforms provide key management services, AWS Key Management Service being one of them. This provides an additional layer of data security from data breaches and unauthorized access by combining the strength of multiple encryption algorithms to protect the data.

AWS Key Management Service

AWS Key Management Service (KMS) is a fully managed service that assists in the creation and management of cryptographic keys and provides a centralized control across a wide range of AWS services and in user’s applications. The maximum size of data that could be encrypted or decrypted using KMS CMK is 4KB. To encrypt/decrypt data more than that KMS uses the concept of envelope encryption.

Below mentioned are the flows required to implement envelope encryption using AWS KMS.

Encrypting a file

For encrypting a file, firstly we need to request KMS to generate a data key. KMS would return an object with an encrypted and plain text version of the data key. Post that, the plain text data key could be used to encrypt the file using some encryption algorithm.
We should destroy the plain text data key from the memory and save the encrypted data key which would be referenced to decrypt the file later.

Encryption.png

Decrypting a file

For decrypting a file, firstly we need to get the plain text data key that was used to encrypt it. Since our CMK in KMS generated the plain text and encrypted data key pair, it can decrypt the encrypted data key and return the plain text key.
The saved encrypted data key would be sent to KMS to be decrypted using our CMK. Post the retrieval of the plain text data key we can use it to decrypt the encrypted file using the decryption algorithm.

decryption.png

Demo Application

Let’s create a demo app in Nodejs to encrypt and decrypt a file using envelope encryption with AWS Key Management Service before storing it in S3 and after retrieving it.

Prerequisites:
  • Create a S3 bucket
  • Create a CMK in AWS KMS
  • Create a DynamoDB table
1. Create an encrypt and decrypt function using a crypto library of nodejs that accepts a plain text data key and file buffer to be encrypted and decrypted.
const encrypt = (buffer, key) => {
  const cipher = crypto.createCipheriv(algorithm, key, iv)
  const result = Buffer.concat([cipher.update(buffer), cipher.final()])
  return result
}
const decrypt = (cipherBuffer, key) => {
  const decipher = crypto.createDecipheriv(algorithm, key, iv)
  const result = Buffer.concat([
    decipher.update(cipherBuffer),
    decipher.final(),
  ])
  return result
}

 

2. In the upload endpoint
// generating a data key from AWS KMS
params = {
  KeyId: process.env.KMS_ARN, // ARN or KeyId of the CMK
  KeySpec: "AES_256", // type of data key to be returned
}
//returns plain and cipher text form of the data key
const dataKey = await kms.generateDataKey(params).promise()
// encrypting the file using the plain text version of the data key
const encryptedBuffer = encrypt(file.data, dataKey.Plaintext)

//making the plain text data key null
for (let i = 0; i < dataKey.Plaintext.length; i++) {
  dataKey.Plaintext[i] = null
}

generateDataKey() of KMS returns plain text and encrypted versions of a data key generated by our KMS CMK. The plain text data key with the file buffer is passed to the encrypt() which returns the encrypted buffer.

 

// storing the encrypted file in S3
params = {
  Bucket: process.env.BUCKET_NAME, // name of the bucket
  Body: encryptedBuffer,
  Key: file.name,
}
await s3.putObject(params).promise()
// creating a record in dynamodb for file and encrypted data key
params = {
  TableName: process.env.TABLE_NAME, // name of the table
  Item: {
    id: uuidv4(),
    encryptedDataKey: dataKey.CiphertextBlob,
    fileName: file.name,
  },
}
await dynamodb.put(params).promise()

The encrypted buffer is stored in S3 and the meta-data of the file and the encrypted data key is stored in DynamoDB.

 

3. In the download endpoint
// Fetching the encrypted data key from dynamodb table
params = {
  TableName: process.env.TABLE_NAME, // table name of dynamodb
  Key: {
    id: fileId,
  },
}
const record = await dynamodb.get(params).promise()
//Fetching the file from S3
params = {
  Bucket: process.env.BUCKET_NAME,
  Key: record.Item.fileName,
}
const s3Res = await s3.getObject(params).promise()

The encrypted data key and the file meta-data are fetched from the DynamoDB post which the encrypted file is fetched from S3.

 

// decrypting the encrypted dataKey
params = {
  CiphertextBlob: record.Item.encryptedDataKey, //encrypted data key
  KeyId: process.env.KMS_ARN, // ARN or KeyId of the CMK
}
const decryptedDataKey = await kms.decrypt(params).promise()
// decrypting the encrypted file from S3
const decryptedBuffer = decrypt(s3Res.Body, decryptedDataKey.Plaintext)

decrypt() of KMS decrypts the encrypted data key with the CMK and returns the plain text version which is passed to our decrypt() with the encrypted file buffer. Our decrypt function decrypts the file and is sent back to the client.

 

Full source code: https://github.com/antstackio/aws-kms-envelope-encryption.

service category

Innovate faster, and go farther with serverless-native application development. Explore limitless possibilities with AntStack's serverless solutions. Empowering your business to achieve your most audacious goals.

Build with us

Author(s)

Tags

Share this blog

Your Digital Journey deserves a great story.

Build one with us.

Recommended Blogs

cookie

These cookies are used to collect information about how you interact with this website and allow us to remember you. We use this information in order to improve and customize your browsing experience and for analytics and metrics about our visitors on this website.

If you decline, your information won’t be tracked when you visit this website. A single cookie will be used in your browser to remember your preference not to be tracked.

Build With Us