Skip to content

Instantly share code, notes, and snippets.

@csobankesmarki
Last active November 19, 2020 07:20
Show Gist options
  • Save csobankesmarki/7a7142c454a7dcf81705b7f8d6a8d707 to your computer and use it in GitHub Desktop.
Save csobankesmarki/7a7142c454a7dcf81705b7f8d6a8d707 to your computer and use it in GitHub Desktop.
register new or verify existing Let's Encrypt account by using secret key
#!/usr/bin/env bash
#
# This file register a new or verify an existing Let's Encrypt account
# Usage:
# re-register.sh <secret key file> <e-mail address>
#
# It will provide the json reply from Let's Encrypt when trying to register an
# account with a key assigned even if the account exists already.
# The json contains the details of the new/existing account.
# (e.g.: registration date, e-mail address, url with the account ID in the header)
# It will not change the e-mail address if the account already exists.
#
# The private key must be provided in PEM format and works with RSA, yet
#
# key components sent in the request are only modulus and exponent
# but private key is required as the request should be signed
if [[ $# -eq 2 ]]; then
keyfile="${1}"
email="${2}"
else
echo "missing filename and/or email address" > /dev/stderr
exit 1
fi
keytype=$(egrep -o "BEGIN [A-Z]+ " ${keyfile} | cut -d ' ' -f 2)
if [[ ${keytype} != "RSA" ]]; then
echo "invalid key type" > /dev/stderr
exit 2
fi
directoryurl="https://acme-v02.api.letsencrypt.org/directory"
directory=$(curl --silent --user-agent "re-register.sh/0.1.0" --header "accept: application/dns-json" "${directoryurl}")
termurl=$(echo "${directory}" | egrep -o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3)
newaccounturl=$(echo "${directory}" | egrep -o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3)
nonceurl=$(echo "${directory}" | egrep -o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3)
exponent=$(openssl pkey -in ${keyfile} -noout -text_pub | grep "^Exponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
if [ "${#exponent}" = "5" ]; then
exponent=0$exponent
fi
e=$(echo ${exponent} | tr 'a-z' 'A-Z' | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf | openssl base64 -e | tr '/+' '_-' | tr -d '= ' | tr -d '\n')
modulus=$(openssl pkey -in ${keyfile} -noout -text_pub | sed -n -e '$d' -e '1,2d' -e 's/ //g' -e 's/://g' -e 'p' | tr '[a-z]' '[A-Z]' | tr -d '\n')
n="$(printf "%s" "$modulus" | tr 'a-z' 'A-Z' | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf | openssl base64 -e | tr '/+' '_-' | tr -d '= ' | tr -d '\n')"
jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
nonce=$(curl --silent --include --user-agent "re-register.sh/0.1.0" -X POST --header "Content-Type: application/jose+json" --header "accept: application/dns-json" --data "" "${nonceurl}" | grep -i "replay-nonce:" | head -n 1 | tr -d "\r\n " | cut -d ':' -f 2)
protected="{\"nonce\": \"${nonce}\", \"url\": \"${newaccounturl}\", \"alg\": \"RS256\", \"jwk\": ${jwk}}"
noncesed="s/\"nonce\": \".*\", \"url\"/\"nonce\": \"${nonce}\", \"url\"/g"
protected64=$(printf "%s" ${protected} | openssl base64 -e | tr '/+' '_-' | tr -d '= ' | tr -d '\n')
payload="{\"resource\": \"newAccount\", \"contact\": [\"${email}\"], \"terms-of-service-agreed\": true, \"agreement\": \"${termurl}\"}"
payload64=$(printf "%s" ${payload} | openssl base64 -e | tr '/+' '_-' | tr -d '= ' | tr -d '\n')
signed64=$(printf "%s" "${protected64}.${payload64}" | openssl dgst -sign ${keyfile} -sha256 | openssl base64 -e | tr '/+' '_-' | tr -d '= ' | tr -d '\n')
body="{\"protected\": \"${protected64}\", \"payload\": \"${payload64}\", \"signature\": \"${signed64}\"}"
curl --silent --include --user-agent "re-register.sh/0.1.0" -X POST --header "Content-Type: application/jose+json" --header "accept: application/dns-json" --data "${body}" "${newaccounturl}" && echo
@csobankesmarki
Copy link
Author

csobankesmarki commented Nov 18, 2020

This file register a new or verify an existing Let's Encrypt account
Usage:
re-register.sh

It will provide the json reply from Let's Encrypt when trying to register an
account with a key assigned even if the account exists already.
The json contains the details of the new/existing account.
(e.g.: registration date, e-mail address, url with the account ID in the header)
It will not change the e-mail address if the account already exists.

The private key must be provided in PEM format and works with RSA, yet

key components sent in the request are only modulus and exponent
but private key is required as the request should be signed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment