Skip to content

Instantly share code, notes, and snippets.

@joostd
Created January 16, 2025 13:33
Show Gist options
  • Save joostd/9c6c3db393cf112d1fe2d87b8db18fc8 to your computer and use it in GitHub Desktop.
Save joostd/9c6c3db393cf112d1fe2d87b8db18fc8 to your computer and use it in GitHub Desktop.
Generate a CSR for an asymmetric key stored in a YubiHSM 2 with OpenSSL and yubihsm-shell
#!/bin/bash
# Generate a Certificate Signing Request (CSR) for an asymmetric key stored in a YubiHSM 2
# Usage:
# ./gencsr.sh <id> <cn>
#
# where <id> is the object ID of the asymmetric key,
# and <cn> is the Common Name of the subject DN in the generated CSR.
ID=$1
CN=$2
# note that the subject DN of the generated CSR will consist of a Common Name only
# other fields will typically be overwritten by the CA anyway
if [[ -z "$ID" ]]; then
echo need an object ID
echo "usage: $0 <id> <cn>"
exit
fi
if [[ -z "$CN" ]]; then
echo need a Common Name
echo "$usage: 0 <id> <cn>"
exit
fi
TMP=$(mktemp -d)
echo === Retrieving Public Key
yubihsm-shell -C yhusb:// -p password -a get-public-key -i $ID > pubkey.pem || { echo "ERROR: Cannot find asymmetric key with ID $ID"; exit ; }
# quick & dirty: extract public key bytes from a EC p256 public key
LEN=$(openssl asn1parse -in pubkey.pem | tail -1 | sed -E 's/.*l= +([0-9]+).*/\1/')
pubkey=$(openssl pkey -in pubkey.pem -pubin -outform der | tail -c $(($LEN-1)) | xxd -p | tr -d '\n')
echo === Getting Key Info
ALG=$(yubihsm-shell -C yhusb:// -p password -a get-object-info -i $ID -t asymmetric-key | grep -o 'algorithm: [[:alnum:]]*' | cut -d' ' -f2)
echo === Algorithm $ALG
case "$ALG" in
rsa2048 | rsa3072 | rsa4096)
signatureAlgorithm="sha256WithRSAEncryption"
SIGALG=rsa-pkcs1-sha256
SIGN=sign-pkcs1v15 ;;
ecp224 | ecp256 | ecp384 | ecp521 | eck256)
signatureAlgorithm="ecdsa-with-SHA256"
SIGALG=ecdsa-sha256
SIGN=sign-ecdsa ;;
ed25519*)
signatureAlgorithm="ED25519"
SIGALG=ed25519
SIGN=sign-eddsa ;;
*)
echo "unknown algorithm $ALG"
exit ;;
esac
echo "=== Generate Certificate Signing Request (CSR)"
# CSR template before signing
cat << EOF > $TMP/tbs.asn1
asn1 = SEQUENCE:CertificationRequestInfo
[CertificationRequestInfo]
version = INTEGER:0x00
subject = SEQUENCE:dn
subjectPKInfo = SEQUENCE:PublicKeyInfo
attributes = IMPLICIT:0,SEQUENCE:Attributes
[dn]
dn = SET:rdn
[rdn]
cn = SEQUENCE:cn
[cn]
cn = OBJECT:commonName
val = FORMAT:UTF8,UTF8String:"$CN"
[PublicKeyInfo]
algorithm = SEQUENCE:$ALG
subjectPublicKey = FORMAT:HEX,BITSTRING:$pubkey
[ecp224]
algorithm = OBJECT:id-ecPublicKey
parameters = OBJECT:secp224r1
[ecp256]
algorithm = OBJECT:id-ecPublicKey
parameters = OBJECT:prime256v1
[ecp384]
algorithm = OBJECT:id-ecPublicKey
parameters = OBJECT:secp384r1
[ecp521]
algorithm = OBJECT:id-ecPublicKey
parameters = OBJECT:secp521r1
[eck256]
algorithm = OBJECT:id-ecPublicKey
parameters = OBJECT:secp256k1
[ed25519]
parameters = OBJECT:ED25519
[rsa2048]
parameters = OBJECT:rsaEncryption
parameters = NULL
[rsa3072]
algorithm = OBJECT:rsaEncryption
parameters = NULL
[rsa4096]
algorithm = OBJECT:rsaEncryption
parameters = NULL
[Attributes]
EOF
# The YubiHSM uses different names for PublicKeyInfo algorithm objects than OpenSSL
#
# YubiHSM OpenSSL
# ------- -------
# ecp224 secp224r1
# ecp256 prime256v1
# ecp384 secp384r1
# ecp521 secp521r1
# eck256 secp256k1
# ecbp256 -
# ecbp384 -
# ecbp512 -
# rsa2048 rsaEncryption
# rsa3072 rsaEncryption
# rsa4096 rsaEncryption
# compile ASN.1
openssl asn1parse -genconf $TMP/tbs.asn1 -out $TMP/tbs.der #>/dev/null
# sign using YubiHSM
echo === Signing $SIGN / $SIGALG
yubihsm-shell -C yhusb:// -p password -a $SIGN -A $SIGALG -i $ID --in $TMP/tbs.der | base64 -d > $TMP/sig.der #2>/dev/null
# double check: verify signature
openssl dgst -sha256 -verify pubkey.pem -signature $TMP/sig.der $TMP/tbs.der
rm $TMP/tbs.der
# Generate CSR, include self-signature
cat << EOF > $TMP/csr.asn1
asn1 = SEQUENCE:csr
[csr]
cri = SEQUENCE:CertificationRequestInfo
signatureAlgorithm = SEQUENCE:signatureAlgorithm
signature = FORMAT:HEX,BITSTRING:$(xxd -p $TMP/sig.der | tr -d '\n')
[signatureAlgorithm]
algorithm = OBJECT:$signatureAlgorithm
parameters = NULL
EOF
rm $TMP/sig.der
cat $TMP/tbs.asn1 | grep -v ^asn1 >> $TMP/csr.asn1
rm $TMP/tbs.asn1
# compile ASN.1
openssl asn1parse -genconf $TMP/csr.asn1 -out $TMP/csr.der >/dev/null
rm $TMP/csr.asn1
# verify signature
openssl req -in $TMP/csr.der -inform der -verify -out csr.pem && openssl req -in csr.pem
rm $TMP/csr.der
rmdir $TMP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment