CoursesAdvanced secrets managementDynamic secrets & crypto engines

Transit: encryption & tokenization as a service

Data keys, convergent encryption, key rotation, and rewrap.

Advanced35 min · lesson 5 of 15

Not every secret is a credential to hand out — sometimes you need to encrypt application data (a column, a token, a file) and the hard part is key management, not the cipher. Vault’s Transit engine is "encryption as a service": applications send plaintext and get ciphertext back, and the keys never leave Vault. This solves the problem that kills most homegrown encryption — key storage, rotation, and access control — by making the key a thing you call an API to use, never a thing you hold.

Encrypt/decrypt without ever holding the key

With Transit, the app calls encrypt with a named key and gets back a versioned ciphertext (prefixed like vault:v3:...); to read it, it calls decrypt. The application never sees key material, so a compromised app leaks data it currently holds but cannot decrypt the archive, and cannot exfiltrate a key that was never there. Access is governed by Vault policy: "this service may encrypt with key orders but may not decrypt" is a legitimate, enforceable statement — write-only encryption for a frontend, decrypt reserved for the batch job.

terminal — Transit encrypt/decrypt
$ vault secrets enable transit
$ vault write -f transit/keys/orders # key material stays in Vault
$ vault write transit/encrypt/orders \
plaintext=$(base64 <<< "4111 1111 1111 1111")
ciphertext vault:v1:8SDd3W...
$ vault write transit/decrypt/orders ciphertext="vault:v1:8SDd3W..."
plaintext NDExMSAxMTExIDExMTEgMTExMQ== # base64 -> original
# the version prefix (v1) is how rotation stays transparent.

Key rotation and rewrap — transparent by design

Because every ciphertext carries its key version, rotation is painless: rotate the key and new writes use v2 while old vault:v1: ciphertexts still decrypt with the retained old version. When you want to fully retire an old key version, the rewrap endpoint re-encrypts existing ciphertext to the latest version without ever exposing plaintext — you feed it vault:v1: and get vault:v2: back, and the data never leaves its encrypted form. Set a min_decryption_version to actually cut off ancient key versions once everything is rewrapped.

Rotate without re-encrypting everything at once
1rotate key
new version v2, v1 retained
2new writes use v2
old vault:v1: still decrypts
3rewrap on read/batch
v1 ciphertext -> v2, no plaintext
4raise min version
v1 finally refused
The version prefix decouples "rotate the key" from "re-encrypt the data" — you can do the second lazily.

Datakeys, convergent encryption, and tokenization

For bulk or local encryption, Transit vends datakeys: a per-object symmetric key returned both in plaintext (use it locally, then discard) and wrapped by the Transit key (store the wrapped copy alongside the data). This is envelope encryption — encrypt gigabytes locally at speed while the master key stays in Vault. Convergent encryption makes identical plaintext produce identical ciphertext so you can still index or dedupe encrypted values (at the cost of leaking equality). And Transit’s tokenization turns a sensitive value into a non-reversible token stored in a mapping, so systems downstream never touch the real data at all — the compliance-scope reducer for PII and card data.

Encrypt-only ≠ safe if decrypt is wide open
The value of Transit is precise access control, so do not undo it by granting decrypt broadly. Give frontends encrypt (and maybe sign) only; reserve decrypt for the specific batch or service that needs cleartext, and rate-limit it — an app with unlimited decrypt is a slow data-exfiltration oracle for anyone who compromises it. And always alarm on bulk-decrypt volume: mass decryption is what a breach looks like from Vault’s side.