Skip to content

Commit 36f6093

Browse files
committed
Check cert serial len + fix output when too big (3.0)
Same as #2014, this is for 3.0 though. OpenSSL shows certificate serial numbers >35 with a LF (0A). Testssl.sh just output that which makes JSON invalid and displays the LF in the terminal too. This PR fixes that (#2010) by adding text filters so that the serial number is not a multiline string. Also this PR introduces a new function: a size check of the cert serial. Below 8 bytes the CAB Forum's lower limit is hit which says the entropy from a CSPRNG should be at least 64 bits. It is assumed that below 8 bytes length this requirement isn't possible to meet (needs to be clarified with Shannon, 8 bytes seems to low to me). The high threshold is according to RFC 5280, Section-4.1.2.2 . See also #2013. The output has changed, so that on the terminal the serial has one line, SHA1 and SHA256 each one line. The new json key is "cert_serialNumberLen".
1 parent 136b941 commit 36f6093

1 file changed

Lines changed: 56 additions & 9 deletions

File tree

testssl.sh

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8083,6 +8083,31 @@ certificate_transparency() {
80838083
return 0
80848084
}
80858085

8086+
# Shortcut for $OPENSSL x509 -noout -in $cert $ossl_command
8087+
# arg1 is the certificate
8088+
# arg2 is -serial | -fingerprint -sha1 | -fingerprint -sha256
8089+
# returns the serial or fingerprint as ASCII
8090+
#
8091+
determine_cert_fingerprint_serial() {
8092+
local cert="$1"
8093+
local ossl_command="$2"
8094+
local result=""
8095+
8096+
result="$($OPENSSL x509 -noout $ossl_command 2>>$ERRFILE <<< "$cert")"
8097+
# remove strings in text output, colon only appear in fingerprints
8098+
result="${result//Fingerprint=}"
8099+
result="${result//serial=}"
8100+
result="${result//:/}"
8101+
result="${result//SHA1 /}"
8102+
result="${result//SHA256 /}"
8103+
# When the serial number is too large we'll get a 0x0a LF after 70 ASCII chars (see #2010).
8104+
# Thus we clean them here so that it is displayed correctly.
8105+
result="${result/[$'\n\r']/}"
8106+
result="${result//[\\]/}"
8107+
safe_echo "$result"
8108+
}
8109+
8110+
80868111
certificate_info() {
80878112
local proto
80888113
local -i certificate_number=$1
@@ -8123,6 +8148,7 @@ certificate_info() {
81238148
local response=""
81248149
local yearstart yearend clockstart clockend y m d
81258150
local gt_825=false gt_825warn=false
8151+
local len_cert_serial=0
81268152

81278153
if [[ $number_of_certificates -gt 1 ]]; then
81288154
[[ $certificate_number -eq 1 ]] && outln
@@ -8383,20 +8409,41 @@ certificate_info() {
83838409
fileout "${jsonID}${json_postfix}" "INFO" "$cert_ext_keyusage"
83848410
fi
83858411

8386-
out "$indent"; pr_bold " Serial / Fingerprints "
8387-
cert_serial="$($OPENSSL x509 -noout -in $HOSTCERT -serial 2>>$ERRFILE | sed 's/serial=//')"
8412+
hostcert="$(<$HOSTCERT)"
8413+
8414+
out "$indent"; pr_bold " Serial "
8415+
cert_serial="$(determine_cert_fingerprint_serial "$hostcert" "-serial")"
83888416
fileout "cert_serialNumber${json_postfix}" "INFO" "$cert_serial"
8417+
out "$cert_serial"
8418+
8419+
len_cert_serial=${#cert_serial}
8420+
len_cert_serial=$(( len_cert_serial / 2 ))
8421+
8422+
if [[ $len_cert_serial -gt 20 ]]; then
8423+
# https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2
8424+
outln
8425+
prln_svrty_low "${spaces}NOT ok: length must not exceed 20 bytes (is: $len_cert_serial bytes)"
8426+
fileout "cert_serialNumberLen${json_postfix}" "LOW" "$len_cert_serial is too long"
8427+
elif [[ $len_cert_serial -lt 8 ]]; then
8428+
# Wording is from https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.8.0.pdf
8429+
prln_svrty_low " NOT ok: length should be >= 64 bits entropy (is: $len_cert_serial bytes)"
8430+
fileout "cert_serialNumberLen${json_postfix}" "LOW" "$len_cert_serial is not enough entropy"
8431+
else
8432+
outln " (OK: length $len_cert_serial)"
8433+
fileout "cert_serialNumberLen${json_postfix}" "INFO" "$len_cert_serial"
8434+
fi
83898435

8390-
cert_fingerprint_sha1="$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha1 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g')"
8391-
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1//SHA1 /}"
8392-
outln "$cert_serial / $cert_fingerprint_sha1"
8436+
out "$indent"; pr_bold " Fingerprints "
8437+
cert_fingerprint_sha1="$(determine_cert_fingerprint_serial "$hostcert" "-fingerprint -sha1")"
8438+
outln "SHA1 $cert_fingerprint_sha1"
8439+
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1}"
83938440

8394-
cert_fingerprint_sha2="$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha256 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g' )"
8395-
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2//SHA256 /}"
8396-
outln "$spaces$cert_fingerprint_sha2"
8441+
cert_fingerprint_sha2="$(determine_cert_fingerprint_serial "$hostcert" "-fingerprint -sha256")"
8442+
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2}"
8443+
outln "${spaces}SHA256 ${cert_fingerprint_sha2}"
83978444

83988445
# " " needs to be converted back to lf in JSON/CSV output
8399-
fileout "cert${json_postfix}" "INFO" "$(< $HOSTCERT)"
8446+
fileout "cert${json_postfix}" "INFO" "$hostcert"
84008447

84018448
[[ -z $CERT_FINGERPRINT_SHA2 ]] && \
84028449
CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2" ||

0 commit comments

Comments
 (0)