Skip to content

KMS Envelope Encryption Architecture

What is Envelope Encryption?

Encrypting large payloads directly with KMS has high network latency, KMS API throttling risks, and per-call costs. Envelope Encryption solves this by: 1. Calling KMS once to generate a symmetric Data Encryption Key (DEK) (plaintext + ciphertext). 2. Using the plaintext DEK locally to encrypt the large payload with AES-256-GCM. 3. Wiping the plaintext DEK from memory and storing the ciphertext DEK alongside the encrypted payload.

Encryption Flow

[Plaintext Data] + [Plaintext DEK] ──(AES-256-GCM)──> [Encrypted Data]
                                                            │
[KMS CMK] ──(GenerateDataKey)──> [Ciphertext DEK] ──────────┼──> Stored Record

Reference Python Implementation (AWS KMS + Cryptography)

import os
import boto3
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

kms = boto3.client('kms', region_name='us-east-1')

def encrypt_payload(data: bytes, kms_key_id: str) -> dict:
    # 1. Generate DEK from KMS
    response = kms.generate_data_key(KeyId=kms_key_id, KeySpec='AES_256')
    plaintext_dek = response['Plaintext']
    ciphertext_dek = response['CiphertextBlob']

    # 2. Encrypt locally using AES-GCM
    aesgcm = AESGCM(plaintext_dek)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, data, None)

    # 3. Wipe plaintext DEK from memory
    del plaintext_dek

    return {
        'ciphertext': ciphertext,
        'ciphertext_dek': ciphertext_dek,
        'nonce': nonce
    }