Back of the Envelope Encryption

The security of your sensitive data, both at rest and in transit, should be a concern of every developer. So, what type of encryption method should you use?

To start, we should define what is Symmetric and Asymmetric encryption.

Symmetric encryption, is a type of cryptography where a single secret key is used for both encrypting plaintext (original data) and decrypting ciphertext (encrypted data). This means that both the sender and the recipient must possess the same secure key for the process to work. Symmetric encryption is known for its speed compared to asymmetric methods, making it well-suited for encrypting large amounts of data. However, the challenge in symmetric encryption lies in the secure sharing and management of the shared secret key.

Asymmetric encryption, also known as public-key cryptography, is a type of cryptography where two mathematically related keys are used – a public key and a private key. The public key can be freely shared and is used for encryption. In contrast, the private key must be kept secret and is used for decryption. This means that anyone can encrypt data using the public key, but only the holder of the corresponding private key can decipher it. Asymmetric encryption is often used for secure key exchange and digital signatures, but is generally slower than symmetric encryption.

So why not do both? Envelope encryption offers the ability to combine symmetric and asymmetric encryption techniques.

Understanding Envelope Encryption

Envelope encryption is a powerful technique where you encrypt your actual data with a unique data key and then encrypt that data key with another key. This layered approach adds security. The top-level key used to encrypt the data key is called the master key. You can even add multiple layers of encryption, where each data key is encrypted by a key above it. At some point, however, your top-level master key needs to remain in plaintext for the decryption process to work.

AWS Key Management Service (KMS) makes it easier to implement.

AWS KMS simplifies the secure storage and management of your master keys. These master keys, called customer master keys (CMKs) within KMS, never leave the FIPS-validated hardware security modules of AWS KMS unencrypted. You interact with CMKs through API calls to AWS KMS.

To leverage envelope encryption with AWS KMS in your applications, follow these steps:

  1. Generate a Data Key:
    Use the GenerateDataKey operation in AWS KMS to obtain a new data encryption key.
  2. Local Encryption:
    Use the plaintext data key to encrypt your data directly within your application.
    ***Crucially, erase the plaintext data key from memory as soon as possible for maximum security.***
  3. Store Encrypted Key:
    Store the encrypted version of the data key (the CiphertextBlob) along with your encrypted data.

This approach works because it offers multiple layers of protection for your data. Encrypting both the data itself and the data key adds an extra layer of security, making it significantly more difficult to breach. Using AWS KMS simplifies the complexities of managing those sensitive keys. By never sending plaintext data keys over the network, you minimize potential exposure and substantially reduce the overall risk to your data.

How to Encrypt using the AWS CLI

To implement envelope encryption with AWS KMS and the AWS CLI, you’ll first create a Customer Master Key (CMK) within KMS. Next, you utilize KMS to generate a data key, which you’ll receive in both plaintext and encrypted forms. The plaintext data key is used to encrypt your actual data using a standard symmetric encryption algorithm. Then you encrypt the data key itself using the CMK managed by KMS. Finally, you securely store the encrypted data and the KMS-encrypted data key. If you want to decrypt, you reverse the process by first using KMS to decrypt the data key and then using the decrypted data key to unlock your original data.

  1. Create a Customer Master Key (CMK):
aws kms create-key --description "My data encryption key"

You will see KeyMetadata in JSON returned. Make note of the KeyId because you will need to reference it to encrypt and decrypt with AWS KMS CLI.

2. Generate a Data Key:

aws kms generate-data-key --key-id <Your KeyId> --key-spec AES_256 

3. Decode the Data Keys:

plaintext_key=$(echo <Plaintext Data Key> | base64 -d) 
$(echo <CiphertextBlob> | base64 -d) > encrypted_data_key.bin

You see the CiphertextBlob is being stored to a file. The main function of the CiphertextBlob is to securely store the encrypted version of your data key. You will need to save this data so that you can later retrieve the encrypted key for decrypting your data. This encrypted data key is essential for the decryption process. You should store it securely, typically alongside your encrypted data.

4. Encrypt your Data:

iv=$(openssl rand -hex 16)  # Generate a random 16-byte IV
openssl enc -aes-256-cbc -in <your_data_file> -out <encrypted_data_file> -K $plaintext_key -iv $iv 

Now Let’s Make Sure We Can Decrypt It

To decrypt your data, first retrieve the stored CiphertextBlob which contains your encrypted data key. Then send this CiphertextBlob to AWS KMS, along with your Customer Master Key (CMK) ID. KMS will decrypt the CiphertextBlob using your CMK and return the original plaintext data key. Finally, use this plaintext data key with your chosen symmetric encryption algorithm to unlock your actual data.

Remember, the CiphertextBlob is a secure container for your data key, not a direct encryption tool. The core purpose of envelope encryption is to protect the plaintext data key from exposure.

encrypted_key=$(cat encrypted_data_key.bin)
ciphertext_blob=$(echo $encrypted_key | base64)

# Decrypt with Key with KMS
response=$(aws kms decrypt --ciphertext-blob fileb://<(echo $ciphertext_blob) --key-id <Your KeyId> --output text --query Plaintext) 

# Extract decrypted key
decrypted_key=$(echo $response | base64 -d)

# Decrypt using OpenSSL with the decrypted key and the same IV
openssl enc -d -aes-256-cbc -in <encrypted_data_file> -out <decrypted_data_file> -K $decrypted_key -iv $iv

How to Automate Envelope Encryption in Your Code

Here’s a simple Python example demonstrating envelope encryption with AWS KMS. We’ll use the Boto3 library, the AWS SDK for Python.

Prerequisites:

  • An AWS account with AWS KMS access.
  • A Customer Master Key (CMK) created in AWS KMS.
  • The Boto3 library installed (pip install boto3).
  • Proper IAM permissions for your code to call AWS KMS operations.
import boto3
import base64
from cryptography.fernet import Fernet

CMK_ID = 'your-cmk-id'

def wipe_data(data: bytes) -> None:  
    random_data = secrets.token_bytes(len(data))
    for i in range(len(data)):
        data[i] = random_data[i]
    del data
    
def encrypt_data(plaintext_data: bytes) -> tuple[bytes, bytes]:
 
    kms_client = boto3.client('kms')

    # Generate a new data key
    response = kms_client.generate_data_key(
        KeyId=CMK_ID,
        KeySpec='AES_256'
    )

    plaintext_data_key = response['Plaintext']
    encrypted_data_key = response['CiphertextBlob']

    fernet_cipher = Fernet(base64.urlsafe_b64encode(plaintext_data_key))
    encrypted_data = fernet_cipher.encrypt(plaintext_data)
    
    wipe_data(plaintext_data_key)

    return encrypted_data, encrypted_data_key
    
def decrypt_data(encrypted_data: bytes, encrypted_data_key: bytes) -> bytes:

    kms_client = boto3.client('kms')

    response = kms_client.decrypt(
        CiphertextBlob=encrypted_data_key
    )
    plaintext_data_key = response['Plaintext']

    fernet_cipher = Fernet(base64.urlsafe_b64encode(plaintext_data_key))
    decrypted_data = fernet_cipher.decrypt(encrypted_data)

    wipe_data(plaintext_data_key)

    return decrypted_data

Start by setting up your CMK ID and importing the necessary libraries for encryption and KMS interaction. The encrypt_data function handles the core process. It connects to AWS KMS and generates a new data key using your CMK. Then, using a symmetric encryption library (like Fernet), it encrypts your data with this data key.

Next, you might want to look into using AWS Secrets Manager or Parameter Store for secure key storage, and carefully consider your choice of cryptography library for the specific security needs of your application.