A legal-tech platform accumulates secrets quickly: per-tenant encryption keys,
vault data-encryption keys, model-provider API tokens, OAuth client secrets,
signing keys for audit logs, HMAC keys for pseudonymization. Each has a different
blast radius when it leaks and a different cost to rotate. A coherent rotation
policy is the difference between "rotated every quarter" as theater and "rotated
every quarter" as a verified, automated, audited operation.
1. Envelope Encryption
Every payload is encrypted under a freshly generated data encryption key
(DEK), and the DEK is itself encrypted (wrapped) under a long-lived
key encryption key (KEK, the "CMK" in AWS / "key" in Azure Key
Vault). Three consequences follow:
The KEK can sit in an HSM and never leave; only the DEK is ever handled in
application memory.
Rotating the KEK does not require re-encrypting the payload — only
re-wrapping each DEK, which is tiny and fast.
Deleting the KEK cryptographically shreds every payload wrapped under it, a
useful primitive for right-to-erasure.
2. Per-Matter CMKs
For high-sensitivity matters, issue a dedicated KEK per matter. Benefits:
Disabling the matter's KEK revokes access to every document in that
matter in one operation.
Audit trails at the KMS layer show exactly which payloads were unwrapped and
by whom; per-matter separation keeps the logs narrow.
Cross-matter queries must explicitly present multiple decrypt permissions,
which the RBAC layer can deny by default.
3. Rotation Without Re-Encrypting Payloads
With envelope encryption, rotation is a rewrap, not a re-encryption:
KMS creates a new KEK version; the old version stays active for decryption.
A background job walks every stored wrapped-DEK, unwraps with the old
version, rewraps with the new version, writes the new wrapped value.
Once all DEKs are rewrapped, the old KEK version is scheduled for retirement
(disabled first, destroyed after a defined retention period).
The payload ciphertext is never touched; only the small wrapped-key blobs move. A
tenant with 10 TB of encrypted documents still rotates in minutes.
4. Example: Rewrap Job
import boto3
kms = boto3.client("kms")
def rewrap(wrapped_dek: bytes, target_kek: str,
encryption_context: dict) -> bytes:
"""Decrypt under the current KEK version, re-encrypt under target_kek."""
dec = kms.decrypt(
CiphertextBlob=wrapped_dek,
EncryptionContext=encryption_context,
)
enc = kms.encrypt(
KeyId=target_kek,
Plaintext=dec["Plaintext"],
EncryptionContext=encryption_context,
)
return enc["CiphertextBlob"]
def rotate_matter(matter_id: str, new_kek_arn: str, store, audit) -> None:
audit.log("kek.rotate.start", matter=matter_id, target=new_kek_arn)
rewrapped = 0
for row in store.iter_vault_rows(matter_id):
ctx = {"matter": matter_id, "token": row.token}
row.wrapped_key = rewrap(row.wrapped_key, new_kek_arn, ctx)
store.update(row)
rewrapped += 1
audit.log("kek.rotate.done", matter=matter_id, target=new_kek_arn,
rewrapped=rewrapped)
# Encryption context binds the wrap to its purpose: a wrapped-DEK for matter A
# cannot be rewrapped / decrypted under matter B's context, even by an operator
# with permissions on both.
5. Application Secrets & Short-Lived Creds
Prefer short-lived credentials — IAM roles, STS tokens,
workload-identity federation — over long-lived static secrets. A credential
that expires in an hour cannot leak for six months.
When a static secret is unavoidable (third-party API token),
store in a secrets manager with automatic rotation (AWS Secrets Manager, Vault,
Akeyless); pin the rotation cadence to the provider's accepted overlap window.
No secrets in environment variables in logs — scrub
environment dumps, exception traces, and process listings at source. Assume any
leaked log will be archived forever.
Signed git commits / signed releases — the keys that
sign releases rotate on their own schedule; track them as first-class secrets.
6. Emergency Rotation (Compromise)
Routine rotation is not a substitute for emergency rotation. When a secret is
compromised:
Revoke immediately — disable the key/token at the
control plane; this should be a one-command operation with a pre-tested runbook.
Issue replacement — new KEK version, new API token,
new signing key as applicable.
Re-wrap or re-issue downstream material — DEKs wrapped
under the compromised KEK must be rewrapped under the new one before the old
is destroyed.
Audit scope of exposure — correlate the compromised
window with KMS decrypt logs, API usage, and access patterns; this is the input
to the breach-notification decision.
Write the retro — how did the secret leave the trust
boundary, and what change closes the path.