HashiCorp Vault — Encrypt & Decrypt

1 minute read

The core Transit operations: encrypt plaintext, decrypt ciphertext, and batch process multiple items.


Encrypt

Plaintext must be base64-encoded before sending to Vault:

# Encrypt a string
vault write transit/encrypt/my-app-key \
    plaintext=$(echo -n "my secret data" | base64)

Expected output:

Key            Value
---            -----
ciphertext     vault:v1:UEhLNVF...encrypted...data
key_version    1

Ciphertext format: vault:v<KEY_VERSION>:<BASE64_ENCRYPTED_DATA>

  • vault: — Prefix identifying Vault-encrypted data
  • v1 — Key version used for encryption
  • The rest — Base64-encoded encrypted payload

Decrypt

vault write transit/decrypt/my-app-key \
    ciphertext="vault:v1:UEhLNVF...encrypted...data"

Expected output:

Key          Value
---          -----
plaintext    bXkgc2VjcmV0IGRhdGE=
# Decode the base64 plaintext
echo "bXkgc2VjcmV0IGRhdGE=" | base64 --decode
# my secret data

One-Liner Patterns

# Encrypt and get just the ciphertext
CIPHERTEXT=$(vault write -field=ciphertext transit/encrypt/my-app-key \
    plaintext=$(echo -n "credit-card-4242" | base64))

# Decrypt and decode in one step
vault write -field=plaintext transit/decrypt/my-app-key \
    ciphertext="$CIPHERTEXT" | base64 --decode

API Usage

# Encrypt via API
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
     --request POST \
     --data "{\"plaintext\": \"$(echo -n 'my secret' | base64)\"}" \
     "$VAULT_ADDR/v1/transit/encrypt/my-app-key" | jq

# Decrypt via API
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
     --request POST \
     --data '{"ciphertext": "vault:v1:UEhLNVF..."}' \
     "$VAULT_ADDR/v1/transit/decrypt/my-app-key" | jq

Batch Operations

Encrypt or decrypt multiple items in a single request:

# Batch encrypt via API
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
     --request POST \
     --data '{
       "batch_input": [
         {"plaintext": "'$(echo -n "secret1" | base64)'"},
         {"plaintext": "'$(echo -n "secret2" | base64)'"},
         {"plaintext": "'$(echo -n "secret3" | base64)'"}
       ]
     }' \
     "$VAULT_ADDR/v1/transit/encrypt/my-app-key" | jq

Response:

{
  "data": {
    "batch_results": [
      {"ciphertext": "vault:v1:abc...", "key_version": 1},
      {"ciphertext": "vault:v1:def...", "key_version": 1},
      {"ciphertext": "vault:v1:ghi...", "key_version": 1}
    ]
  }
}

Batch decryption works the same way with batch_input containing ciphertext fields.


Context and Associated Data

Add context for key derivation (useful with derived keys):

# Encrypt with context
vault write transit/encrypt/my-app-key \
    plaintext=$(echo -n "my secret" | base64) \
    context=$(echo -n "user-123" | base64)

# Must provide the same context to decrypt
vault write transit/decrypt/my-app-key \
    ciphertext="vault:v1:..." \
    context=$(echo -n "user-123" | base64)

Application Integration — Python

import hvac
import base64

client = hvac.Client(url='http://127.0.0.1:8200', token='my-token')

# Encrypt
plaintext = base64.b64encode(b"credit-card-4242-4242").decode()
result = client.secrets.transit.encrypt_data(
    name='my-app-key',
    plaintext=plaintext
)
ciphertext = result['data']['ciphertext']
print(f"Ciphertext: {ciphertext}")

# Store ciphertext in database...

# Decrypt
result = client.secrets.transit.decrypt_data(
    name='my-app-key',
    ciphertext=ciphertext
)
decoded = base64.b64decode(result['data']['plaintext']).decode()
print(f"Plaintext: {decoded}")
# credit-card-4242-4242

Quick Reference

Command Description
vault write transit/encrypt/KEY plaintext=BASE64 Encrypt
vault write transit/decrypt/KEY ciphertext=CIPHER Decrypt
vault write -field=ciphertext transit/encrypt/KEY ... Get ciphertext only
Batch via API batch_input Encrypt/decrypt multiple items

Next: Key Rotation