HashiCorp Vault — Transit Exercises

3 minute read

Practice encryption, key rotation, envelope encryption, and signing.


Exercise 1 — Basic Encrypt/Decrypt

Task: Encrypt sensitive data and decrypt it.

  1. Enable the Transit engine
  2. Create an encryption key called app-key
  3. Encrypt the string “SSN:123-45-6789”
  4. Decrypt it back to plaintext
  5. Verify you get the original string
Show Answer
# 1. Enable Transit
vault secrets enable transit

# 2. Create key
vault write -f transit/keys/app-key

# 3. Encrypt
CIPHERTEXT=$(vault write -field=ciphertext transit/encrypt/app-key \
    plaintext=$(echo -n "SSN:123-45-6789" | base64))
echo "Ciphertext: $CIPHERTEXT"

# 4. Decrypt
PLAINTEXT_B64=$(vault write -field=plaintext transit/decrypt/app-key \
    ciphertext="$CIPHERTEXT")

# 5. Verify
echo "$PLAINTEXT_B64" | base64 --decode
# SSN:123-45-6789

Exercise 2 — Key Rotation and Rewrap

Task: Rotate an encryption key and rewrap existing data.

  1. Encrypt “my-secret” with key version 1
  2. Rotate the key
  3. Encrypt “another-secret” — verify it uses version 2
  4. Rewrap the first ciphertext to version 2
  5. Verify the old v1 ciphertext still decrypts
  6. Set min_decryption_version to 2 and try to decrypt the old v1 ciphertext
Show Answer
# 1. Encrypt with v1
CT_V1=$(vault write -field=ciphertext transit/encrypt/app-key \
    plaintext=$(echo -n "my-secret" | base64))
echo "V1 ciphertext: $CT_V1"
# vault:v1:...

# 2. Rotate
vault write -f transit/keys/app-key/rotate

# 3. Encrypt with v2
CT_V2=$(vault write -field=ciphertext transit/encrypt/app-key \
    plaintext=$(echo -n "another-secret" | base64))
echo "V2 ciphertext: $CT_V2"
# vault:v2:...

# 4. Rewrap v1 ciphertext
CT_REWRAPPED=$(vault write -field=ciphertext transit/rewrap/app-key \
    ciphertext="$CT_V1")
echo "Rewrapped: $CT_REWRAPPED"
# vault:v2:... (new ciphertext, same plaintext)

# 5. Old v1 still decrypts
vault write -field=plaintext transit/decrypt/app-key \
    ciphertext="$CT_V1" | base64 --decode
# my-secret ✅

# 6. Set min_decryption_version
vault write transit/keys/app-key/config min_decryption_version=2

vault write transit/decrypt/app-key ciphertext="$CT_V1"
# Error! Cannot decrypt — below minimum decryption version

# But rewrapped ciphertext works
vault write -field=plaintext transit/decrypt/app-key \
    ciphertext="$CT_REWRAPPED" | base64 --decode
# my-secret ✅

Exercise 3 — Envelope Encryption

Task: Use envelope encryption to encrypt a file.

  1. Generate a datakey
  2. Use the plaintext key to encrypt a file with OpenSSL
  3. Store the encrypted key alongside the encrypted file
  4. Discard the plaintext key
  5. Later: decrypt the datakey via Vault, then decrypt the file
Show Answer
# Create a test file
echo "This is sensitive data that needs encryption" > /tmp/sensitive.txt

# 1. Generate datakey
RESPONSE=$(vault write -format=json transit/datakey/plaintext/app-key)
PLAINTEXT_KEY=$(echo "$RESPONSE" | jq -r .data.plaintext)
ENCRYPTED_KEY=$(echo "$RESPONSE" | jq -r .data.ciphertext)

# 2. Encrypt the file
echo "$PLAINTEXT_KEY" | base64 --decode > /tmp/datakey.bin
openssl enc -aes-256-cbc -pbkdf2 \
    -in /tmp/sensitive.txt \
    -out /tmp/sensitive.txt.enc \
    -pass file:/tmp/datakey.bin

# 3. Store encrypted key
echo "$ENCRYPTED_KEY" > /tmp/sensitive.txt.key

# 4. Discard plaintext key
rm /tmp/datakey.bin
unset PLAINTEXT_KEY

# 5. Decrypt: recover the datakey
RECOVERED_KEY=$(vault write -field=plaintext transit/decrypt/app-key \
    ciphertext="$(cat /tmp/sensitive.txt.key)")
echo "$RECOVERED_KEY" | base64 --decode > /tmp/datakey.bin

# Decrypt the file
openssl enc -aes-256-cbc -pbkdf2 -d \
    -in /tmp/sensitive.txt.enc \
    -out /tmp/sensitive-recovered.txt \
    -pass file:/tmp/datakey.bin

cat /tmp/sensitive-recovered.txt
# This is sensitive data that needs encryption

# Cleanup
rm /tmp/datakey.bin /tmp/sensitive.txt.enc /tmp/sensitive.txt.key

Exercise 4 — Digital Signatures

Task: Sign and verify data.

  1. Create an ed25519 signing key
  2. Sign the message “deploy-v2.3.1”
  3. Verify the signature
  4. Tamper with the message and verify again — should fail
Show Answer
# 1. Create signing key
vault write transit/keys/deploy-signer type=ed25519

# 2. Sign
SIGNATURE=$(vault write -field=signature transit/sign/deploy-signer \
    input=$(echo -n "deploy-v2.3.1" | base64))
echo "Signature: $SIGNATURE"

# 3. Verify (should be valid=true)
vault write transit/verify/deploy-signer \
    input=$(echo -n "deploy-v2.3.1" | base64) \
    signature="$SIGNATURE"
# valid: true ✅

# 4. Tamper with message
vault write transit/verify/deploy-signer \
    input=$(echo -n "deploy-v2.3.1-TAMPERED" | base64) \
    signature="$SIGNATURE"
# valid: false ❌

Exercise 5 — Transit Policy

Task: Write a policy that allows an application to encrypt and decrypt with a specific key, but NOT rotate or delete the key.

Show Answer
# app-transit-policy.hcl

# Encrypt and decrypt
path "transit/encrypt/app-key" {
  capabilities = ["update"]
}
path "transit/decrypt/app-key" {
  capabilities = ["update"]
}

# Generate data keys (for envelope encryption)
path "transit/datakey/plaintext/app-key" {
  capabilities = ["update"]
}

# Read key info (but not the key material)
path "transit/keys/app-key" {
  capabilities = ["read"]
}

# Explicitly deny key management
path "transit/keys/app-key/rotate" {
  capabilities = ["deny"]
}
path "transit/keys/app-key/config" {
  capabilities = ["deny"]
}
vault policy write app-transit app-transit-policy.hcl
TOKEN=$(vault token create -policy=app-transit -ttl=1h -field=token)

# Can encrypt
VAULT_TOKEN=$TOKEN vault write transit/encrypt/app-key \
    plaintext=$(echo -n "test" | base64)
# Success ✅

# Cannot rotate
VAULT_TOKEN=$TOKEN vault write -f transit/keys/app-key/rotate
# Error: permission denied ❌

What’s Next?

With Transit mastered, let’s move to Vault as a Certificate Authority with the PKI secrets engine.

Next Chapter: PKI Secrets Engine