Envelope encryption is how you protect data at rest, but it’s not just about encrypting your data with a random key. It’s about managing that random key securely.

Let’s see how it works in practice with AWS Key Management Service (KMS).

Imagine you have a sensitive document, confidential.txt, that you want to encrypt.

$ cat confidential.txt
This is highly sensitive information.

First, you need a Data Key. This is a symmetric encryption key, generated on-the-fly, that will actually encrypt your data. KMS can generate this for you.

aws kms generate-data-key --key-id arn:aws:kms:us-east-1:111122223333:key/YOUR_KMS_KEY_ID --key-spec AES_256 --query 'Plaintext' --output text > data_key.bin

This command gives you a Plaintext Data Key (data_key.bin). This is the key that will encrypt your confidential.txt.

Now, you use that Data Key to encrypt your actual data. We’ll use openssl for this.

openssl enc -aes-256-cbc -salt -in confidential.txt -out confidential.encrypted -pass file:data_key.bin

Notice we’re using -pass file:data_key.bin. This tells openssl to read the encryption key from that file.

After this, confidential.txt is encrypted. But what about the Data Key itself? If you just stored data_key.bin alongside confidential.encrypted, you haven’t gained much. This is where KMS comes back in.

KMS can encrypt your Data Key using a Customer Master Key (CMK) that you manage. This CMK is asymmetric (or symmetric, depending on configuration, but the principle holds) and its private key never leaves KMS.

aws kms encrypt --key-id arn:aws:kms:us-east-1:111122223333:key/YOUR_KMS_KEY_ID --plaintext fileb://data_key.bin --query 'CiphertextBlob' --output blob > data_key.encrypted.bin

Now, you can securely store confidential.encrypted and data_key.encrypted.bin. The data_key.bin file, the actual key that decrypts your data, can be safely deleted.

rm data_key.bin

When you need to decrypt confidential.txt, you reverse the process. First, you decrypt the Data Key using KMS.

aws kms decrypt --key-id arn:aws:kms:us-east-1:111122223333:key/YOUR_KMS_KEY_ID --ciphertext-blob fileb://data_key.encrypted.bin --query 'Plaintext' --output text > decrypted_data_key.bin

This decrypted_data_key.bin is the same content as the original data_key.bin. Now you can use this to decrypt your data.

openssl enc -aes-256-cbc -d -in confidential.encrypted -out decrypted_confidential.txt -pass file:decrypted_data_key.bin

And there you have it: decrypted_confidential.txt contains your original message.

The core idea is that your actual data is encrypted with a transient, randomly generated Data Key (AES-256 is common). This Data Key is then encrypted by a KMS Customer Master Key (CMK). The CMK’s private key is managed by AWS and never exposed, providing a secure way to protect the Data Key itself. This layered approach is "envelope encryption."

The real magic is that KMS handles the secure generation and storage of the CMK, and the encryption/decryption of your Data Keys. You don’t have to worry about managing raw private keys or complex key rotation for your data encryption keys.

One of the most surprising aspects is how granularly you can control access to your CMKs. You can define IAM policies that specify exactly which principals (users, roles, services) can use a CMK, and for what operations (generate data key, encrypt, decrypt). This means you can grant a specific application role permission to only decrypt data, and not to generate new data keys or manage the CMK itself, significantly reducing the blast radius if that role’s credentials were compromised.

The next step is understanding how to integrate this into applications, typically using SDKs that abstract away the manual aws kms commands for generating and decrypting data keys.

Want structured learning?

Take the full Cryptography course →