Last active
August 21, 2024 21:13
-
-
Save jay0lee/9865cee7fb4f3ad57fb65d14fb6e52a1 to your computer and use it in GitHub Desktop.
Resets and fully configures the PIV app of a Yubikey for mTLS connections
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 | |
### | |
### Shell script to reset Yubikey PIV app and then fully generate and setup a | |
### non-exportable private key on the Yubikey that's ready to make mTLS | |
### requests. | |
### | |
### Running on Ubuntu, the following packages are needed: | |
### | |
### sudo apt update; sudo apt install opensc yubikey-manager gnutls-bin libengine-pkcs11-openssl | |
### | |
### Example run: | |
### | |
### $ bash yk-mtls-setup.sh | |
### Are you sure you want to reset the PIV app on the attached Yubikey? (y/N) y | |
### New PUK set. | |
### New PIN set. | |
### New PUK: 5aKVNt6f New PIN: gZVGgL3z | |
### Generating private key on Yubikey... | |
### Generating certificate on Yubikey... | |
### Exporting certificate from Yubikey... | |
### Calculating fingerprint... | |
### Certificate fingerprint is (copy this down): | |
### | |
### 7pbGBd15XuADg2YPGlIYzZWaq/ekJQbvRVi8P3YEEZs | |
### | |
### Cert to use (copy this down): | |
### | |
### pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=c17adfc5b0b478ec;token=yubikey-mtls-setup-script;pin-value=gZVGgL3z | |
### | |
### (end of output by command) | |
### | |
### The fingerprint value is useful server-side (like in CAA access levels). | |
### The "Cert to use value can be used directly by curl to perform mTLS using the new client cert. | |
### We'll copy the cert value to a variable to use with curl: | |
### | |
### export cert="pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=99135642b242eb71;token=yubikey-mtls-setup-script;pin-value=N3hwrizb" | |
### | |
### Now we can tell curl to GET a demo website using mTLS and our cert stored on the Yubikey: | |
### | |
### curl --cert "$cert" https://certauth.idrix.fr/json/ | jq . | |
### | |
### the output shows us that mTLS with the Yubikey is working and sending our certificate. | |
### the JSON value shows our client certificate's subject value and fingerprint. Notice | |
### the fingerprint is in a different format from the fingerprint above that Google uses. | |
use_slot="9a" | |
default_mk="010203040506070801020304050607080102030405060708" | |
default_pin="123456" | |
default_puk="12345678" | |
algorithm="ECCP256" | |
cert_subject="yubikey-mtls-setup-script" | |
valid_chars="a-zA-Z0-9" | |
newpuk=$(dd if=/dev/urandom count=1 status=none | tr -dc "$valid_chars" | fold -w 8 | head -n 1) | |
newpin=$(dd if=/dev/urandom count=1 status=none | tr -dc "$valid_chars" | fold -w 8 | head -n 1) | |
PARAMS="" | |
while (( "$#" )); do | |
case "$1" in | |
--slot) | |
# Yubikey PIV slot to use. Default is 9a. | |
# https://docs.yubico.com/yesdk/users-manual/application-piv/slots.html | |
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | |
use_slot=$2 | |
shift 2 | |
else | |
echo "Error: Argument for $1 is missing" >&2 | |
exit 1 | |
fi | |
;; | |
--newpin) | |
# 8 char PIN to set for Yubikey slot. Default is a random string. | |
# Notice this also ends up in the cert string we output at the end. | |
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | |
newpin="$2" | |
shift 2 | |
else | |
echo "Error: Argument for $1 is missing" >&2 | |
exit 1 | |
fi | |
;; | |
--newpuk) | |
# 8 char PUK to set for Yubikey slot. Default is a random string. | |
# this is only used if you forget and lock the PIN. | |
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | |
newpuk=$2 | |
shift 2 | |
else | |
echo "Error: Argument for $1 is missing" >&2 | |
exit 1 | |
fi | |
;; | |
--algorithm) | |
# Crypto Algorithm to use for private key. Default is ECCP256. | |
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | |
algorithm=$2 | |
shift 2 | |
else | |
echo "Error: Argument for $1 is missing" >&2 | |
exit 1 | |
fi | |
;; | |
--subject) | |
# Subject value to set on the client certificate. Default is a string | |
# that IDs this script. Should not contain spaces or special chars | |
# beyond @ . - and _ | |
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | |
cert_subject=$2 | |
shift 2 | |
else | |
echo "Error: Argument for $1 is missing" >&2 | |
exit 1 | |
fi | |
;; | |
*) | |
echo "Error: Unsupported argument $1" >&2 | |
exit 1 | |
;; | |
esac | |
done | |
# set positional arguments in their proper place | |
eval set -- "$PARAMS" | |
if [ ! -z "$(echo -n "$cert_subject" | tr -d 'a-zA-Z0-9-_@.')" ]; then | |
echo "ERROR: --subject should only be a-z, A-Z, 0-9, -, _, @ and ." | |
exit 1 | |
fi | |
if ! [ -x "$(command -v jq)" ]; then | |
echo 'Error: jq is not installed and is needed for JSON parsing.' >&2 | |
exit 1 | |
fi | |
if ! [ -x "$(command -v openssl)" ]; then | |
echo 'Error: openssl is not installed and is needed for hashing functions.' >&2 | |
exit 1 | |
fi | |
if ! [ -x "$(command -v ykman)" ]; then | |
echo 'Error: ykman is not installed and is needed for setting up the Yubikey. You need to install the Ubuntu yubikey-manager package.' >&2 | |
exit 1 | |
fi | |
if ! [ -x "$(command -v p11tool)" ]; then | |
echo 'Error: p11tool is not installed. You need to install the Ubuntu opensc package.' >&2 | |
exit 1 | |
fi | |
read -p "Are you sure you want to reset the PIV app on the attached Yubikey? (y/N) " yn | |
if [ "$yn" != "y" ]; then | |
"Giving up then. Enter exactly y next time." | |
exit 1 | |
fi | |
ykman piv reset --force &> /dev/null | |
ykman piv access change-management-key \ | |
--protect \ | |
--pin "$default_pin" \ | |
--management-key "$default_mk" | |
ykman piv access change-puk --puk "$default_puk" --new-puk "$newpuk" | |
ykman piv access change-pin --pin "$default_pin" --new-pin "$newpin" | |
echo -e "New PUK: ${newpuk} New PIN: ${newpin}" | |
echo "Generating private key on Yubikey..." | |
pubkey=$(ykman piv keys generate \ | |
--pin "$newpin" \ | |
--algorithm "$algorithm" \ | |
--format "PEM" \ | |
--pin-policy "ALWAYS" \ | |
--touch-policy "NEVER" \ | |
"$use_slot" -) | |
echo "Generating certificate on Yubikey..." | |
echo -e "$pubkey" | ykman piv certificates generate \ | |
--hash-algorithm "SHA256" \ | |
--valid-days 36500 \ | |
--pin "$newpin" \ | |
--subject "$cert_subject" \ | |
"$use_slot" - | |
echo "Exporting certificate from Yubikey..." | |
pubcert=$(ykman piv certificates "export" \ | |
--format PEM \ | |
"$use_slot" -) | |
echo "Calculating fingerprint..." | |
fingerprint=$(echo -e "$pubcert" \ | |
| openssl x509 -outform DER \ | |
| openssl dgst -sha256 -binary - \ | |
| openssl base64 \ | |
| tr -d "=") | |
echo -e "Certificate fingerprint is (copy this down):\n\n ${fingerprint}\n" | |
encoded_subject=$(jq -rn --arg x "$cert_subject" '$x|@uri') | |
pkcs11_cert=$(p11tool --list-tokens \ | |
| grep "URL:" \ | |
| grep "$encoded_subject" \ | |
| cut -d' ' -f2) | |
pkcs11_cert="${pkcs11_cert};pin-value=${newpin}" | |
echo -e "Cert to use (copy this down):\n\n ${pkcs11_cert}\n" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment