Recipe: KMS envelope encryption design
A practical pattern for encrypting large payloads with a cloud KMS without hitting rate limits or size caps.
Problem
Cloud KMS services impose strict limits: 4 KB max plaintext per encrypt call, single-digit millisecond latency floor, and aggressive request quotas. Encrypting a 10 MB file directly is impossible. Envelope encryption solves this by splitting the workload into a key-wrapping layer and a data-encryption layer.
Design
- Generate a random 256-bit data encryption key (DEK) locally using a CSPRNG.
- Encrypt the plaintext with the DEK using AES-256-GCM. Store the ciphertext, nonce, and auth tag.
- Call the KMS encrypt API once, passing only the DEK as plaintext. The KMS returns the encrypted DEK (wrapped key).
- Package the wrapped DEK alongside the ciphertext, nonce, and tag into a single envelope structure.
- To decrypt: call KMS decrypt on the wrapped DEK, then use the recovered DEK to decrypt the ciphertext.
Envelope format
{
"version": 1,
"kms_key_id": "arn:aws:kms:...",
"wrapped_dek": "<base64>",
"ciphertext": "<base64>",
"nonce": "<base64>",
"auth_tag": "<base64>"
}Trade-offs
- KMS never sees the plaintext data — only the DEK.
- One KMS call per envelope, regardless of payload size.
- DEK rotation requires re-wrapping, not re-encrypting data.
- Local DEK generation trusts the host CSPRNG.