Created
October 27, 2025 18:01
-
-
Save GuyBarros/4204f54fdc06cbcd114d475fdb18e167 to your computer and use it in GitHub Desktop.
datakey.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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