Skip to content

Instantly share code, notes, and snippets.

@hongkongkiwi
Created January 25, 2020 08:33
Show Gist options
  • Save hongkongkiwi/b7670aaa44ed84eeb8e7886338967008 to your computer and use it in GitHub Desktop.
Save hongkongkiwi/b7670aaa44ed84eeb8e7886338967008 to your computer and use it in GitHub Desktop.
Bash script to check ssl certificate validity, also handles OCSP & CRL checking.
#!/bin/sh
OPENSSL_BIN="openssl"
CERT_NAME="$1"
# CERT_CHAIN=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
# CRL_DER_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
if [ "$(uname)" == "Darwin" ]; then
DATE_BIN="gdate"
else
DATE_BIN="date"
fi
get_ocsp_url() {
if [ -f $1 ]; then
OCSP_URL=`"$OPENSSL_BIN" x509 -noout -ocsp_uri -in "$1"`
else
OCSP_URL=`"$OPENSSL_BIN" \
x509 \
-noout \
-ocsp_uri \
-connect $1`
fi
RET_CODE=$?
echo "$OCSP_URL"
return $RET_CODE
}
get_remote_cert() {
CERT=`"$OPENSSL_BIN" \
s_client \
-connect $1 \
</dev/null 2>/dev/null \
| sed -n '/-----BEGIN/,/-----END/p'`
RET_CODE=$?
[ -z "$CERT" ] && return $RET_CODE
[ $RET_CODE -ne 0 ] && return $RET_CODE
if [ -z $2 ]; then
CERT_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
echo "$CERT_NAME"
else
CERT_NAME=$2
echo "$CERT"
fi
echo "$CERT" >$CERT_NAME
return $RET_CODE
}
extract_cert_chain() {
if [ -f $1 ]; then
CERT_CHAIN=`"$OPENSSL_BIN" \
s_client \
-showcerts \
-in "$1" \
</dev/null 2>/dev/null \
| awk '/^-----BEGIN CERT/,/^-----END CERT/'`
else
CERT_CHAIN=`"$OPENSSL_BIN" \
s_client \
-showcerts \
-connect $1 \
</dev/null 2>/dev/null \
| awk '/^-----BEGIN CERT/,/^-----END CERT/'`
fi
RET_CODE=$?
echo "$CERT_CHAIN"
[ ! -z $2 ] && echo "$CERT_CHAIN" >$2
return $RET_CODE
}
get_crl_url() {
if [ -f $1 ]; then
CRL_URL=`"$OPENSSL_BIN" \
x509 \
-noout \
-text \
-in "$1" \
</dev/null 2>/dev/null \
| grep -A 4 'X509v3 CRL Distribution Points' \
| grep -Eo '(http|https)://[^"]*'`
else
{ echo >&2 "ERROR: Cannot find certificate $1 to extract crl from"; exit 1; }
fi
RET_CODE=$?
echo "$CRL_URL"
return $RET_CODE
}
download_crl() {
CRL_DER_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
CRL_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
wget -q -O "$CRL_DER_NAME" "$1"
RET_CODE=$?
"$OPENSSL_BIN" crl -inform DER -in $CRL_DER_NAME -outform PEM -out $CRL_NAME >/dev/null < /dev/null
[ -f "$CRL_DER_NAME" ] && rm "$CRL_DER_NAME"
echo "$CRL_NAME"
return $RET_CODE
# openssl crl -inform DER -in "$CRL_DER_NAME" -outform PEM -out "$2"
# rm "$CRL_DER_NAME"
}
verify_crl() {
# SERIAL_NUMBER=`"$OPENSSL_BIN" x509 -in $1 -noout -serial`
CRL_CHAIN_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
cat "$1" "$2" > "$CRL_CHAIN_NAME"
#echo >&2 $("$OPENSSL_BIN" crl -inform DER -text -in "$CRL_DER_NAME" | grep "2")
#echo >&2 $(openssl crl -inform DER -in "$CRL_DER_NAME" -outform PEM)
#grep "$SERIAL_NUMBER"
"$OPENSSL_BIN" verify -crl_check -CAfile "$CRL_CHAIN_NAME" "$3" </dev/null 2>/dev/null
RET_CODE=$?
[ -f "$CRL_CHAIN_NAME" ] && rm "$CRL_CHAIN_NAME"
return $RET_CODE
}
check_expiry_date() {
CERT_EXPIRE=`"$OPENSSL_BIN" x509 -enddate -noout -in $1 | cut -d "=" -f2`
now_date=$("$DATE_BIN" +"%Y%m%d") # = 20130718
expire_date=$("$DATE_BIN" -d "$CERT_EXPIRE" +"%Y%m%d") # = 20130715
echo "$CERT_EXPIRE"
if [ $now_date -ge $expire_date ]; then
return 1
else
return 0
fi
}
check_issue_date() {
CERT_START=`"$OPENSSL_BIN" x509 -startdate -noout -in $1 | cut -d "=" -f2`
now_date=$("$DATE_BIN" +"%Y%m%d") # = 20130718
start_date=$("$DATE_BIN" -d "$CERT_EXPIRE" +"%Y%m%d") # = 20130715
echo "$CERT_START"
if [ $now_date -lt $start_date ]; then
return 1
else
return 0
fi
}
if [ ! -f "$CERT_NAME" ]; then
REMOTE_NAME=`echo "$CERT_NAME" | sed -e 's|^[^/]*//||' -e 's|/.*$||'`
REMOTE_PORT=`echo "$REMOTE_NAME" | cut -d ":" -f2`
if [ ! -z "$REMOTE_PORT" ] && [[ "$REMOTE_NAME" == "$REMOTE_PORT" ]]; then
REMOTE_NAME="$REMOTE_NAME:443"
elif [[ $REMOTE_PORT == "0" ]]; then
REMOTE_NAME="`echo "$REMOTE_NAME" | cut -d ":" -f1`:443"
fi
echo "Getting remote certificate from $REMOTE_NAME..."
CERT_NAME=`get_remote_cert "$REMOTE_NAME"`
if [ -z "$CERT_NAME" ]; then
{ echo >&2 "ERROR: Cannot extract certificate"; exit 1; }
else
echo " - Got remote certificate"
fi
echo "Getting remote certificate chain..."
CHAIN_NAME=$(mktemp /tmp/output.XXXXXXXXXX) || { echo "Failed to create temp file"; exit 1; }
RESULT=`extract_cert_chain "$REMOTE_NAME" "$CHAIN_NAME"`
if [ -z "$RESULT" ]; then
[ -f "$CHAIN_NAME" ] && rm "$CHAIN_NAME"
{ echo "Cannot extract certificate chain from certificate"; exit 1; }
else
echo " - Got remote certificate chain"
fi
else
exit 1
fi
EXPIRY_DATE=`check_expiry_date "$CERT_NAME"` || { echo >&2 "ERROR: Certificate is expired!"; exit 1; }
START_DATE=`check_issue_date "$CERT_NAME"` || { echo >&2 "ERROR: Certificate start date is later than todays clock!!"; exit 1; }
echo "Extracting CRL url from certificate..."
CRL_URL=`get_crl_url "$CERT_NAME"`
if [ ! -z "$CRL_URL" ]; then
echo " - CRL URL: $CRL_URL"
echo "Downloading CRL list..."
CRL_NAME=`download_crl "$CRL_URL"`
echo " - Successfully downloaded"
echo "Verifying CRL to list"
verify_crl "$CERT_NAME" "$CHAIN_NAME" "$CRL_NAME"
echo " - Certificate not present on CRL list"
[ -f "$CRL_NAME" ] && rm "$CRL_NAME"
else
echo " - This certificate has no CRL URL - ignoring"
fi
echo "Extracting OCSP url from certificate..."
OCSP_URL=`get_ocsp_url "$CERT_NAME"`
if [ ! -z "$OCSP_URL" ]; then
echo " - OCSP URL: $OCSP_URL"
echo "Checking OCSP from $OCSP_URL ..."
# Useful info here https://raymii.org/s/articles/OpenSSL_Manually_Verify_a_certificate_against_an_OCSP.html
# Check OCSP of certificate
OCSP_STATUS=`"$OPENSSL_BIN" ocsp -issuer -no_nonce "$CHAIN_NAME" -cert "$CERT_NAME" -url "$OCSP_URL" 2>/dev/null`
TRIMMED_STATUS=`echo "$OCSP_STATUS" | sed 's/^[^:]*: //g'`
if [ "$TRIMMED_STATUS" == "ERROR: No Status found." ]; then
echo "Certificate is valid"
elif [ "$TRIMMED_STATUS" == "good" ]; then
echo "Certificate is valid"
elif [ "$TRIMMED_STATUS" == "revoked" ]; then
echo "Certificate is revoked"
exit 1
elif [ "$TRIMMED_STATUS" == "unknown" ]; then
echo >&2 "OCSP cannot verify this certificate"
exit 3
elif [ "$TRIMMED_STATUS" == "unauthorized" ]; then
echo >&2 "OCSP cannot verify this certificate"
exit 3
else
echo >&2 "ERROR: We do not know how to handle this status!"
echo "$OCSP_STATUS"
OCSP_EXTENDED_STATUS=`"$OPENSSL_BIN" ocsp -issuer "$CHAIN_NAME" -cert "$CERT_NAME" -text -url "$OCSP_URL" 2>/dev/null`
echo >&2 "$OCSP_EXTENDED_STATUS"
exit 4
fi
else
echo " - This certificate has no OCSP URL - ignoring"
fi
if [ -z $OCSP_URL ] && [ -z $CRL_URL ]; then
echo >&2 "This certificate has no OCSP URL nor CRL URL there is no way to verify validity."
exit 0
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment