DEV Community

loading...

A Little Bit of Action with AWS KMS

sergiodn profile image Sergio Díaz ・6 min read

TL;DR

After reading this blog post, you will have an overview on how to implement Encryption as a Service using AWS KMS.

Overview

In the previous post, we used Hashicorp Vault to perform encryption and decryption operations. Now we are going to have a look to a different approach. Why? Although Vault is an open-source solution backed by a great community. It might not be for you. For example, you need to manage it and ensure that it will have 100% uptime. Failing to do so could jeopardize your application's availability. This will also require engineering resources, so depending on priorities of your company, you might consider different options. With AWS KMS, you will be able to worry less about managing a business critical service while obtaining the advantages of Encryption as a Service.

Quick Start

The following diagram gives a general overview of how this implementation works:

https://dev-to-uploads.s3.amazonaws.com/i/nly75rjcqqlj168xxxna.png

Encryptor

This service identifies a user. Then, encrypts a message that is received via console prompt. When we encrypt a message, the encryptor receives a cipher-text blob (bytes) from KMS. However, the encryptor base64-encodes it before returning the message to the client. This is done to transport the data in an easier way.

Decryptor

This service identifies a user. Then, decrypts an encrypted message (which is base64-encoded) that is received via console prompt.

AWS KMS

This service will manage encryption and decryption for us. But first we need to create it. So let’s use CloudFormation (CF) to request the resources we need.

Requesting the resources in AWS using CF

You can fin the source code here. By using the repo’s CF template, we will create the following resources:

https://dev-to-uploads.s3.amazonaws.com/i/et16y00sl2ea7v3gms8k.png

theKey (CMK)

A logical key that represents the top of a key hierarchy. We use it to perform encryption and decryption operations. By default is a symmetric key, so it can do both: encrypt and decrypt.

theAlias

This is the alias for our key. This is one is helpful to reference a key using a human friendly format. For example, You can use alias/the-alias instead of a UUID. Once an alias has been associated with theKey, the alias can be used in place of the ARN.

iCanEncryptStuffGroup and iCanDecryptStuffGroup

These IAM groups have a policy to allow KMS to encrypt or decrypt respectively.

EncryptorUser and DecryptorUser

These users are able to encrypt or decrypt according to the group they belong to.

EncryptorUserAccessKey and DecryptorUserAccessKey

These credentials allow the associated user to use the AWS API. Normally, we would create an IAM role and attach it to the EC2 instance that runs our application, but we are using local scripts [0].

Deployment

Now it’s time to deploy our infrastructure. Let’s run ./deploy.sh and you should be able to create the resources. Just make sure you update the variables accordingly and to have an AWS account with enough privileges to run the CF template.

If you see an output like the following, we are ready to go.

{
"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "DecryptorUserAccessKey",
"Value": "AKIAXXXXXXXXXXXXXXXX"
},
{
"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "DecryptorUserSecretAccessKey",
"Value": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
},
{
"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "EncryptorUserAccessKey",
"Value": "AKIAXXXXXXXXXXXXXXXX"
},
{

"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "EncryptorUserSecretAccessKey",
"Value": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
},
{
"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "theKeyAlias",
"Value": "alias/kms-poc"
},
{
"ExportingStackId": "arn:aws:cloudformation:us-east-2:XXX:stack/kms-poc/a59778da-fe1b-11ea-adc1-0242ac120002",
"Name": "theKeyId",
"Value": "a59778da-fe1b-11ea-adc1-0242ac120002"
}

Create a .env file and use env.example for some inspiration. You should be able to get all the info you need from the previous output.

Note: For the KEY_ID env variable’s value, we can use either theKeyId’s or the theKeyAlias’s value.

How to Encrypt

  • Run python3 encryptor.py
  • Use either alice or bob to “authenticate”
  • Write a message to encrypt
  • Hit Enter

You should see a base64 output like this:

AQICAHhQUp2GIUg1Tf/r2I9nmNWsIJGDrRYieOviWRC0SLz/bwEXoiPmO1CgXGPhO5su0nhJAAAAbjBsBgkqhkiG9w0BBwagXzBdAgEAMFgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9eb1WUEj8kK/WBjvAgEQgCs9K6dPBPgWZ+ZqKqXdIt1S/CUE1Xj9fcUq9vo95Mw/6XKv8yQkciWBnH55

How to Decrypt

  • Copy the output from the encryptor and proceed to run the decryptor by doing: python3 decryptor.py
  • Use the same user you used to encrypt earlier during the process (alice or bob)
  • Paste the base64 output
  • If everything went as planned, you should see something like:
Username:

alice

Hello alice, please enter your message to decrypt:

AQICAHhQUp2GIUg1Tf/r2I9nmNWsIJGDrRYieOviWRC0SLz/bwEXoiPmO1CgXGPhO5su0nhJAAAAbjBsBgkqhkiG9w0BBwagXzBdAgEAMFgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9eb1WUEj8kK/WBjvAgEQgCs9K6dPBPgWZ+ZqKqXdIt1S/CUE1Xj9fcUq9vo95Mw/6XKv8yQkciWBnH55

Your decrypted message: this is a secret

Encryption Context

Now try encrypting a message as alice, but decrypting the message as bob. Even though we are using the same key and we have the appropriate permissions, we get an InvalidCiphertextException. This happens because we use a different EncryptionContext while encrypting and decrypting. Even though this parameter is optional, you should use it. This value can help us understand why a given master key was used and which operation was done. This will be handy when we are looking to the logs.

While the EncryptionContext doesn’t need to be kept as a secret, it helps to know that you are encrypting and decrypting between services in a “conscious” way. For example, if your application is handling messages for alice, but by mistake receives a message from bob, the decryption operation will fail. With that being said, the Encryption + EncryptionContext would be like having both: belt and suspenders.

Key Rotation

When using KMS this functionality can be seen as an advantage or disadvantage. For example, if you use EnableKeyRotation while creating a AWS::KMS::Key, KMS automatically creates a new key material for it, and rotates it every year. If for some reason you need to do it more often, you would need to do a manual rotation. Another thing to add is that KMS retains all the key versions until you delete it. In other words, you cannot delete an old version of the CMK.

Other things to Consider

Key Strategy

For this example we only used one key to encrypt and decrypt the user's messages. What would happen if we have millions of users that are encrypting millions of messages? In this case, you will need to adopt a Key Hierarchy Model that meets the needs for your organization. If you want to know more about this, I suggest starting here.

Vendor Lock-in

While AWS API is straight-forward, it would be interesting to know how you could migrate to another Key Management System without having the problem to re-encrypt every single blob that you have saved so far.

Disaster Recovery (DR)

If you can't get access to the crypto keys, you cannot do what your application does. So it might be worth looking on how to implement a multi-region strategy in case of an outage.

Wrapping it Up

As you could see, setting up and performing basic operations with AWS KMS is straight forward. Another advantage is that you will not worry about the service's uptime. This is great, especially if your business relies on those encryption and decryption operations. Nonetheless, you still need to put thought into things such as defining an adequate encryption context, coming up with a key strategy, analyzing how you could avoid vendor lock-in, and placing a disaster recovery strategy.

Reference

[0]https://docs.aws.amazon.com/IAM/latest/UserGuide/id.html

Discussion (2)

pic
Editor guide
Collapse
gonzasestopal profile image
Collapse
sergiodn profile image
Sergio Díaz Author

But then it can turn into AWS Tragic if you don't think for a vendor lock-in plan!