Skip to content

Instantly share code, notes, and snippets.

@GuyBarros
Created October 27, 2025 18:01
Show Gist options
  • Save GuyBarros/4204f54fdc06cbcd114d475fdb18e167 to your computer and use it in GitHub Desktop.
Save GuyBarros/4204f54fdc06cbcd114d475fdb18e167 to your computer and use it in GitHub Desktop.
datakey.sh
#!/usr/bin/env bash
set -euo pipefail
# --- Config (override via env or CLI) -----------------------------------------
VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:8200}"
VAULT_TOKEN="${VAULT_TOKEN:-root}"
VAULT_NAMESPACE="${VAULT_NAMESPACE:-}" # e.g., "admin" or "" for root
TRANSIT_PATH="${TRANSIT_PATH:-transit}" # mount path of transit (e.g., "transit")
KEY_NAME="${KEY_NAME:-dek-demo}" # transit key name (must exist)
MESSAGE="${MESSAGE:-hello-envelope}" # message to encrypt
# Optional: pass MESSAGE as first arg
if [[ "${1:-}" != "" ]]; then MESSAGE="$1"; fi
# --- Helpers ------------------------------------------------------------------
b64() { printf %s "$1" | base64 | tr -d '\n'; }
hdrs=(
-H "X-Vault-Token: ${VAULT_TOKEN}"
-H "Content-Type: application/json"
)
if [[ -n "$VAULT_NAMESPACE" ]]; then
hdrs+=(-H "X-Vault-Namespace: ${VAULT_NAMESPACE}")
fi
need() { command -v "$1" >/dev/null || { echo "Missing dependency: $1"; exit 1; }; }
need jq; need openssl; need curl
# --- 1) Request a one-time data key (plaintext + wrapped) ---------------------
echo "-> Requesting Transit data key from: ${TRANSIT_PATH}/datakey/plaintext/${KEY_NAME}"
resp="$(curl -sS -X POST \
"${VAULT_ADDR}/v1/${TRANSIT_PATH}/datakey/plaintext/${KEY_NAME}" \
"${hdrs[@]}" \
--data '{}')"
# Extract fields
data_key_b64="$(jq -r '.data.plaintext' <<<"$resp")"
wrapped_key="$(jq -r '.data.ciphertext' <<<"$resp")"
key_version="$(jq -r '.data.key_version' <<<"$resp")"
if [[ -z "$data_key_b64" || "$data_key_b64" == "null" || -z "$wrapped_key" || "$wrapped_key" == "null" ]]; then
echo "ERROR: Did not receive data key plaintext and/or wrapped key. Full response:"
echo "$resp"
exit 1
fi
echo " Got key_version=$key_version"
# Convert DEK to hex for OpenSSL
data_key_hex="$(printf %s "$data_key_b64" | base64 -d | xxd -p -c 9999)"
# --- 2) Encrypt MESSAGE with AES-256-CTR using the plaintext data key --------
# We use CTR mode for simplicity (no salt, fixed IV length). If you prefer AEAD,
# switch to AES-256-GCM and handle the tag explicitly.
iv_hex="$(openssl rand -hex 16)" # 16 bytes IV for AES-CTR
cipher_bin="$(mktemp)"
plain_bin="$(mktemp)"
trap 'rm -f "$cipher_bin" "$plain_bin"' EXIT
printf %s "$MESSAGE" > "$plain_bin"
echo "-> Encrypting locally with OpenSSL AES-256-CTR"
openssl enc -aes-256-ctr -K "$data_key_hex" -iv "$iv_hex" -nosalt \
-in "$plain_bin" -out "$cipher_bin"
cipher_b64="$(base64 < "$cipher_bin" | tr -d '\n')"
iv_b64="$(printf %s "$iv_hex" | xxd -r -p | base64 | tr -d '\n')"
# This JSON is what you'd store/transmit:
# - wrapped_data_key: can be unwrapped by Vault later
# - iv: base64 IV used for encryption
# - ciphertext: base64 ciphertext produced locally
envelope_json="$(jq -n \
--arg wrapped "$wrapped_key" \
--arg iv "$iv_b64" \
--arg ct "$cipher_b64" \
--arg ver "$key_version" \
'{wrapped_data_key:$wrapped, iv:$iv, ciphertext:$ct, key_version:($ver|tonumber)}'
)"
echo "Envelope (store this safely):"
echo "$envelope_json"
# --- 3) DECRYPT PHASE ---------------------------------------------------------
# Simulate retrieval: parse the envelope we just produced
wrapped_from_env="$(jq -r '.wrapped_data_key' <<<"$envelope_json")"
iv_b64_from_env="$(jq -r '.iv' <<<"$envelope_json")"
ct_b64_from_env="$(jq -r '.ciphertext' <<<"$envelope_json")"
# 3a) Ask Vault to unwrap the data key (decrypt the wrapped key)
echo "-> Unwrapping data key with Vault: ${TRANSIT_PATH}/decrypt/${KEY_NAME}"
unwrap_body="$(jq -n --arg c "$wrapped_from_env" '{ciphertext:$c}')"
unwrap_resp="$(curl -sS -X POST \
"${VAULT_ADDR}/v1/${TRANSIT_PATH}/decrypt/${KEY_NAME}" \
"${hdrs[@]}" \
--data "$unwrap_body")"
dek_plain_b64="$(jq -r '.data.plaintext' <<<"$unwrap_resp" 2>/dev/null || true)"
if [[ -z "$dek_plain_b64" || "$dek_plain_b64" == "null" ]]; then
echo "ERROR: Failed to unwrap data key. Response:"
echo "$unwrap_resp"
exit 2
fi
dek_hex="$(printf %s "$dek_plain_b64" | base64 -d | xxd -p -c 9999)"
iv_hex2="$(printf %s "$iv_b64_from_env" | base64 -d | xxd -p -c 9999)"
# 3b) Decrypt ciphertext locally with OpenSSL using unwrapped key + IV
echo "-> Decrypting locally with OpenSSL AES-256-CTR"
ct_bin2="$(mktemp)"
trap 'rm -f "$cipher_bin" "$plain_bin" "$ct_bin2"' EXIT
printf %s "$ct_b64_from_env" | base64 -d > "$ct_bin2"
out="$(openssl enc -d -aes-256-ctr -K "$dek_hex" -iv "$iv_hex2" -nosalt -in "$ct_bin2")"
echo "Decrypted plaintext:"
printf '%s\n' "$out"
# Optional integrity check (for demo)
if [[ "$out" == "$MESSAGE" ]]; then
echo "✅ Round-trip success: decrypted message matches original."
else
echo "❌ Mismatch: decrypted message differs from original."
exit 3
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment