← Docs

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

  1. Generate a random 256-bit data encryption key (DEK) locally using a CSPRNG.
  2. Encrypt the plaintext with the DEK using AES-256-GCM. Store the ciphertext, nonce, and auth tag.
  3. Call the KMS encrypt API once, passing only the DEK as plaintext. The KMS returns the encrypted DEK (wrapped key).
  4. Package the wrapped DEK alongside the ciphertext, nonce, and tag into a single envelope structure.
  5. 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.