Last active
December 21, 2023 12:54
-
-
Save Xophmeister/d62605c2f503d3dc45587750fd34c8e7 to your computer and use it in GitHub Desktop.
Demonstration of asymmetric encryption using Amazon KMS
This file contains 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 | |
# 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