Browse Source

Added EC account key support #145 and added option #147

pull/152/head
srvrco 9 years ago
parent
commit
fbf9782cbf
1 changed files with 105 additions and 12 deletions
  1. +105
    -12
      getssl

+ 105
- 12
getssl View File

@ -134,10 +134,12 @@
# 2016-10-22 Add EC signing for secp384r1 and secp521r1 (the latter not yet supported by Let's Encrypt
# 2016-10-22 Add option to create a new private key for every cert (REUSE_PRIVATE_KEY="true" by default)
# 2016-10-22 Combine EC signing, Private key reuse and archive permissions (1.67)
# 2016-10-25 added CHECK_REMOTE_WAIT option ( to pause before final remote check)
# 2016-10-25 Added EC account key support ( prime256v1, secp384r1 ) (1.68)
# ----------------------------------------------------------------------------------------
PROGNAME=${0##*/}
VERSION="1.67"
VERSION="1.68"
# defaults
CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl"
@ -158,6 +160,7 @@ USE_SINGLE_ACL="false"
CHECK_ALL_AUTH_DNS="false"
DNS_WAIT=10
DNS_EXTRA_WAIT=""
CHECK_REMOTE_WAIT=0
PUBLIC_DNS_SERVER=""
CHALLENGE_CHECK_TYPE="http"
DEACTIVATE_AUTH="false"
@ -490,6 +493,40 @@ get_os() { # function to get the current Operating System
debug "detected os type = $os"
}
get_signing_params() { # get signing parameters from key
skey=$1
if [ "$(grep -c "RSA PRIVATE KEY" "$skey")" -gt 0 ]; then # RSA key
pub_exp64=$(openssl rsa -in "${skey}" -noout -text | grep publicExponent | grep -oE "0x[a-f0-9]+" | cut -d'x' -f2 | hex2bin | urlbase64)
pub_mod64=$(openssl rsa -in "${skey}" -noout -modulus | cut -d'=' -f2 | hex2bin | urlbase64)
jwk='{"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}'
jwkalg="RS256"
signalg="sha256"
elif [ "$(grep -c "EC PRIVATE KEY" "$skey")" -gt 0 ]; then # Elliptic curve key.
crv="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')"
case "$crv" in
P-256) jwkalg="ES256" ; signalg="sha256" ;;
P-384) jwkalg="ES384" ; signalg="sha384" ;;
P-521) jwkalg="ES512" ; signalg="sha512" ;;
*) error_exit "invalid curve algorithm type $crv";;
esac
pubtext="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' | tr -d ": \n\r")"
mid=$(( (${#pubtext} -2) / 2 + 2 ))
debug "pubtext = $pubtext"
echo "$pubtext" | cut -b 3-$mid
echo "$pubtext" | cut -b $((mid+1))-${#pubtext}
x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64)
y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64)
jwk='{"crv":"'"$crv"'","kty":"EC","x":"'"$x64"'","y":"'"$y64"'"}'
debug "jwk $jwk"
else
error_exit "Invlid key file"
fi
thumbprint="$(printf "%s" "$jwk" | openssl sha -sha256 -binary | urlbase64)"
debug "jwk alg = $jwkalg"
debug "jwk = $jwk"
debug "thumbprint $thumbprint"
}
graceful_exit() { # normal exit function.
clean_up
exit
@ -614,8 +651,8 @@ revoke_certificate() { #revoke a certificate
debug "revoking cert $REVOKE_CERT"
debug "using key $REVOKE_KEY"
ACCOUNT_KEY="$REVOKE_KEY"
pub_exp64=$(openssl rsa -in "${REVOKE_KEY}" -noout -text | grep publicExponent | grep -oE "0x[a-f0-9]+" | cut -d'x' -f2 | hex2bin | urlbase64)
pub_mod64=$(openssl rsa -in "${REVOKE_KEY}" -noout -modulus | cut -d'=' -f2 | hex2bin | urlbase64)
# need to set the revoke key as "account_key" since it's used in send_signed_request.
get_signing_params "$REVOKE_KEY"
TEMP_DIR=$(mktemp -d)
debug "revoking from $CA"
rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64)
@ -661,15 +698,17 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p
debug nonce "$nonce"
# Build header with just our public key and algorithm information
header='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}}'
header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}'
# Build another header which also contains the previously received nonce and encode it as urlbase64
protected='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}, "nonce": "'"${nonce}"'"}'
protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}'
protected64="$(printf '%s' "${protected}" | urlbase64)"
debug protected "$protected"
# Sign header with nonce and our payload with our private key and encode signature as urlbase64
signed64="$(printf '%s' "${protected64}.${payload64}" | openssl dgst -sha256 -sign "${ACCOUNT_KEY}" | urlbase64)"
# signed64="$(printf '%s' "${protected64}.${payload64}" | openssl dgst -"$signalg" -sign "${ACCOUNT_KEY}" | urlbase64)"
# signed64="$(sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg")"
sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg"
# Send header + extended header + payload + signature to the acme-server
body='{"header": '"${header}"', "protected": "'"${protected64}"'", "payload": "'"${payload64}"'", "signature": "'"${signed64}"'"}'
@ -703,6 +742,63 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p
done
}
sign_string() { #sign a string with a given key and algorithm and return urlbase64
# sets the result in variable signed64
str=$1
key=$2
signalg=$3
if [ "$(grep -c "RSA PRIVATE KEY" "$key")" -gt 0 ]; then # RSA key
signed64="$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "${ACCOUNT_KEY}" | urlbase64)"
elif [ "$(grep -c "EC PRIVATE KEY" "$key")" -gt 0 ]; then # Elliptic curve key.
signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}')
debug "EC signature $signed"
if [ "${signed:4:4}" == "0220" ]; then #sha256
R=$(echo "$signed" | cut -c 9-72)
part2=$(echo "$signed" | cut -c 73-)
elif [ "${signed:4:4}" == "0221" ]; then #sha256
R=$(echo "$signed" | cut -c 11-74)
part2=$(echo "$signed" | cut -c 75-)
elif [ "${signed:4:4}" == "0230" ]; then #sha384
R=$(echo "$signed" | cut -c 9-104)
part2=$(echo "$signed" | cut -c 105-)
elif [ "${signed:4:4}" == "0231" ]; then #sha384
R=$(echo "$signed" | cut -c 11-106)
part2=$(echo "$signed" | cut -c 107-)
elif [ "${signed:6:4}" == "0241" ]; then #sha512
R=$(echo "$signed" | cut -c 11-140)
part2=$(echo "$signed" | cut -c 141-)
elif [ "${signed:6:4}" == "0242" ]; then #sha512
R=$(echo "$signed" | cut -c 11-142)
part2=$(echo "$signed" | cut -c 143-)
else
error_exit "error in EC signing couldn't get R from $signed"
fi
debug "R $R"
if [ "${part2:0:4}" == "0220" ]; then #sha256
S=$(echo "$part2" | cut -c 5-68)
elif [ "${part2:0:4}" == "0221" ]; then #sha256
S=$(echo "$part2" | cut -c 7-70)
elif [ "${part2:0:4}" == "0230" ]; then #sha384
S=$(echo "$part2" | cut -c 5-100)
elif [ "${part2:0:4}" == "0231" ]; then #sha384
S=$(echo "$part2" | cut -c 7-102)
elif [ "${part2:0:4}" == "0241" ]; then #sha512
S=$(echo "$part2" | cut -c 5-136)
elif [ "${part2:0:4}" == "0242" ]; then #sha512
S=$(echo "$part2" | cut -c 5-136)
else
error_exit "error in EC signing couldn't get S from $signed"
fi
debug "S $S"
signed64=$(printf '%s' "${R}${S}" | hex2bin | urlbase64 )
debug "encoded RS $signed64"
result=$(which "$1" 2>/dev/null)
fi
}
signal_exit() { # Handle trapped signals
case $1 in
INT)
@ -927,7 +1023,7 @@ if [[ $_REVOKE -eq 1 ]]; then
fi
# get latest agreement from CA (as default)
AGREEMENT=$(curl -I ${CA}/terms 2>/dev/null | awk '$1 ~ "Location:" {print $2}'|tr -d '\r')
AGREEMENT=$(curl -I "${CA}/terms" 2>/dev/null | awk '$1 ~ "Location:" {print $2}'|tr -d '\r')
# if nothing in command line, print help and exit.
if [ -z "$DOMAIN" ] && [ ${_CHECK_ALL} -ne 1 ]; then
@ -1260,11 +1356,7 @@ fi
# use account key to register with CA
# currently the code registers every time, and gets an "already registered" back if it has been.
# public component and modulus of key in base64
pub_exp64=$(openssl rsa -in "${ACCOUNT_KEY}" -noout -text | grep publicExponent | grep -oE "0x[a-f0-9]+" | cut -d'x' -f2 | hex2bin | urlbase64)
pub_mod64=$(openssl rsa -in "${ACCOUNT_KEY}" -noout -modulus | cut -d'=' -f2 | hex2bin | urlbase64)
thumbprint="$(printf '{"e":"%s","kty":"RSA","n":"%s"}' "${pub_exp64}" "${pub_mod64}" | openssl sha -sha256 -binary | urlbase64)"
get_signing_params "$ACCOUNT_KEY"
if [ "$ACCOUNT_EMAIL" ] ; then
regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
@ -1602,6 +1694,7 @@ fi
# Check if the certificate is installed correctly
if [[ ${CHECK_REMOTE} == "true" ]]; then
sleep "$CHECK_REMOTE_WAIT"
# shellcheck disable=SC2086
CERT_REMOTE=$(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null)
CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null)


Loading…
Cancel
Save