Skip to content

Instantly share code, notes, and snippets.

@Xophmeister
Last active December 21, 2023 12:54
Show Gist options
  • Save Xophmeister/d62605c2f503d3dc45587750fd34c8e7 to your computer and use it in GitHub Desktop.
Save Xophmeister/d62605c2f503d3dc45587750fd34c8e7 to your computer and use it in GitHub Desktop.
Demonstration of asymmetric encryption using Amazon KMS
#!/usr/bin/env bash
# Demonstration of asymmetric cryptography, using Amazon KMS as the
# asymmetric key store and OpenSSL for client-side symmetric encryption.
#
# THIS SHOULD NOT BE CONSIDERED A SECURE IMPLEMENTATION.
set -euo pipefail
get-public-key() {
# Fetch the asymmetric public key from KMS
# We're only interested in the key; we discard the other metadata and
# make assumptions. However, this metadata is important, in general.
local key_id="$1"
aws kms get-public-key --key-id "${key_id}" \
| jq -r '.PublicKey' \
| base64 --decode
}
create-data-key() {
# Create a 256-bit (32 byte) symmetric key for encryption
openssl rand 32
}
encrypt-data-key() {
# Encrypt the data key (from stdin) with the KMS public key
# We assume that the public key is an RSA key that supports
# RSAES_OAEP_SHA_256 encryption, which is enabled with the respective
# -pkeyopt arguments to OpenSSL, below.
local key_id="$1"
openssl pkeyutl -inkey <(get-public-key "${key_id}") \
-pubin \
-encrypt \
-pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256
}
decrypt-data-key() {
# Decrypt the data key (from file) using KMS
local key_id="$1"
local encrypted_data_key_file="$2"
aws kms decrypt --key-id "${key_id}" \
--encryption-algorithm RSAES_OAEP_SHA_256 \
--ciphertext-blob "fileb://${encrypted_data_key_file}" \
| jq -r '.Plaintext' \
| base64 --decode
}
encrypt() {
# Encrypt data (from stdin) using a hybrid cryptosystem:
# * A random, symmetric data key is generated
# * This key is encrypted using the KMS public key
# * The data is encrypted using the data key
# * The encrypted data key and encrypted data are returned together,
# serialised into a JSON payload
local key_id="$1"
# WARNING The plaintext data key is written to a temporary file, which
# can be read by anyone. This is just a demonstration of the protocol,
# it SHOULD NOT BE CONSIDERED A SECURE IMPLEMENTATION.
local data_key_file="$(mktemp)"
create-data-key > "${data_key_file}"
cat <<-JSON
{
"data_key": "$(
encrypt-data-key "${key_id}" < "${data_key_file}" \
| base64 --wrap 0
)",
"ciphertext": "$(
openssl enc -aes-256-cbc \
-pbkdf2 \
-pass "file:${data_key_file}" \
| base64 --wrap 0
)"
}
JSON
rm "${data_key_file}"
}
decrypt() {
# Decrypt JSON-serialised payload (from stdin) using a hybrid
# cryptosystem:
# * Extract the encrypted data key and ciphertext
# * Decrypt the data key using KMS
# * Decrypt the ciphertext using the decrypted data key
local key_id="$1"
local workdir="$(mktemp --directory)"
# Split stdin and extract the encrypted data key and ciphertext
tee >(jq -r '.data_key' | base64 --decode > "${workdir}/key.enc") \
>(jq -r '.ciphertext' | base64 --decode > "${workdir}/ciphertext") \
>/dev/null
decrypt-data-key "${key_id}" "${workdir}/key.enc" > "${workdir}/key"
openssl enc -d -aes-256-cbc \
-pbkdf2 \
-pass "file:${workdir}/key" \
-in "${workdir}/ciphertext"
rm -rf "${workdir}"
}
main() {
# Usage: kms.sh (encrypt|decrypt) KMS_KEY
# Encrypt/decrypt stdin using the given KMS asymmetric key
local mode="$1"
local key_id="$2"
case "${mode}" in
encrypt) encrypt "${key_id}";;
decrypt) decrypt "${key_id}";;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment