Skip to content

Commit 67afa6c

Browse files
committed
MOre points added to complete intermediate cert section
* UI feed back for expiration date of intermediates: 20 days: HIGH, 40 days: MEDIUM * also in JSON/CSV * list the end date of validity * works for >1 intermediates too * section moved to the end of certificate_info() * renamed <cert#${certificate_number}> --> <hostCert#${certificate_number}> to avoid coinfusion with intermediate certs * removed blanks in return values of determine_dates_certificate
1 parent 5eee672 commit 67afa6c

1 file changed

Lines changed: 94 additions & 66 deletions

File tree

testssl.sh

Lines changed: 94 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6468,9 +6468,8 @@ run_server_preference() {
64686468
local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto=""
64696469
local default_cipher=""
64706470
local limitedsense="" supported_sslv2_ciphers
6471-
local -a offered_cipher offered_proto
64726471
local proto_ossl proto_txt proto_hex cipherlist i
6473-
local -i ret=0 j sclient_success str_len
6472+
local -i ret=0 j sclient_success
64746473
local list_fwd="DHE-RSA-SEED-SHA:SEED-SHA:DES-CBC3-SHA:RC4-MD5:DES-CBC-SHA:RC4-SHA:AES128-SHA:AES128-SHA256:AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:AES256-SHA256:ECDHE-RSA-DES-CBC3-SHA:ECDHE-RSA-AES128-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:ADH-AES256-GCM-SHA384:AECDH-AES128-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-AES128-SHA"
64756474
local list_reverse="ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-RC4-SHA:AECDH-AES128-SHA:ADH-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-DES-CBC3-SHA:AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-DES-CBC3-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA256:AES128-SHA:RC4-SHA:DES-CBC-SHA:RC4-MD5:DES-CBC3-SHA:SEED-SHA:DHE-RSA-SEED-SHA"
64766475
tls_list_fwd="c0,2c, c0,30, 00,9f, cc,a9, cc,a8, cc,aa, c0,2b, c0,2f, 00,9e, c0,24, c0,28, 00,6b, c0,23, c0,27, 00,67, c0,0a, 00,04, 00,05, 00,09, 00,0a, 00,9a, 00,96,
@@ -8335,13 +8334,15 @@ determine_cert_fingerprint_serial() {
83358334
result="${result//Fingerprint=}"
83368335
result="${result//serial=}"
83378336
result="${result//:/}"
8337+
result="${result//SHA1 /}"
8338+
result="${result//SHA256 /}"
83388339
safe_echo "$result"
83398340
}
83408341

83418342
# Returns startdate, enddate, diffseconds, days2expire as CSVs as strings
83428343
# arg1: human readable text string for certificate (openssl x509 -text -noout)
83438344
#
8344-
determine_dates_certificate () {
8345+
determine_dates_certificate() {
83458346
local cert_txt="$1"
83468347
local startdate enddate yearnow y m d yearstart clockstart yearend clockend
83478348
local diffseconds=0 days2expire=0
@@ -8360,8 +8361,8 @@ determine_dates_certificate () {
83608361
# Now we extract a date block and a time block which we need for later output
83618362
startdate="$(parse_date "$startdate" +"%F %H:%M" "%b %d %T %Y %Z")"
83628363
enddate="$(parse_date "$enddate" +"%F %H:%M" "%b %d %T %Y %Z")"
8363-
read yearstart clockstart <<< "$startdate"
8364-
read yearend clockend <<< "$enddate"
8364+
read -r yearstart clockstart <<< "$startdate"
8365+
read -r yearend clockend <<< "$enddate"
83658366
debugme echo "$yearstart, $clockstart"
83668367
debugme echo "$yearend, $clockend"
83678368
y=$(( ${yearend:0:4} - ${yearstart:0:4} ))
@@ -8384,7 +8385,7 @@ determine_dates_certificate () {
83848385
days2expire=$((days2expire / 3600 / 24 ))
83858386
diffseconds=$(( $(parse_date "$enddate" "+%s" $'%F %H:%M') - $(parse_date "$startdate" "+%s" $'%F %H:%M') ))
83868387
fi
8387-
safe_echo "$startdate, $enddate, $diffseconds, $days2expire, $yearstart"
8388+
safe_echo "$startdate,$enddate,$diffseconds,$days2expire,$yearstart"
83888389
}
83898390

83908391

@@ -8436,6 +8437,7 @@ certificate_info() {
84368437
local yearstart
84378438
local gt_398=false gt_398warn=false
84388439
local gt_825=false gt_825warn=false
8440+
local first=true
84398441
local badocsp=1
84408442

84418443
if [[ $number_of_certificates -gt 1 ]]; then
@@ -8445,7 +8447,7 @@ certificate_info() {
84458447
pr_headline "Server Certificate #$certificate_number"
84468448
[[ -z "$sni_used" ]] && pr_underline " (in response to request w/o SNI)"
84478449
outln
8448-
json_postfix=" <cert#${certificate_number}>"
8450+
json_postfix=" <hostCert#${certificate_number}>"
84498451
spaces=" "
84508452
else
84518453
spaces=" "
@@ -8600,7 +8602,7 @@ certificate_info() {
86008602
*GOST*|*gost*) short_keyAlgo="GOST";;
86018603
*dh*|*DH*) short_keyAlgo="DH" ;;
86028604
*) pr_fixme "don't know $cert_key_algo "
8603-
let ret++ ;;
8605+
((ret++)) ;;
86048606
esac
86058607
out "$short_keyAlgo "
86068608
# https://tools.ietf.org/html/rfc4492, https://www.keylength.com/en/compare/
@@ -8776,12 +8778,12 @@ certificate_info() {
87768778
fileout "cert_serialNumber${json_postfix}" "INFO" "$cert_serial"
87778779

87788780
cert_fingerprint_sha1="$(determine_cert_fingerprint_serial "$HOSTCERT" "-fingerprint -sha1")"
8779-
outln "$cert_serial / $cert_fingerprint_sha1"
8780-
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1//SHA1 /}"
8781+
outln "$cert_serial / SHA1 $cert_fingerprint_sha1"
8782+
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1}"
87818783

87828784
cert_fingerprint_sha2="$(determine_cert_fingerprint_serial "$HOSTCERT" "-fingerprint -sha256")"
8783-
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2//SHA256 /}"
8784-
outln "$spaces$cert_fingerprint_sha2"
8785+
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2}"
8786+
outln "${spaces}SHA256 ${cert_fingerprint_sha2}"
87858787

87868788
# " " needs to be converted back to lf in JSON/CSV output. watch out leading/ending line containting "CERTIFICATE"
87878789
fileout "cert${json_postfix}" "INFO" "$(< $HOSTCERT)"
@@ -9060,56 +9062,6 @@ certificate_info() {
90609062
# https://certs.opera.com/03/ev-oids.xml
90619063
# see #967
90629064

9063-
9064-
# There might be >1 certificate, so we split intermediatecerts.pem e.g. into
9065-
# intermediatecert1.crt, intermediatecert2.cert.
9066-
#FIXME: This is somewhat redundant code. We do similar stuff elsewhere, e.g. in extract_certificates()
9067-
# and run_hpkp() but don't keep the result
9068-
9069-
# Store all of the text output of the intermediate certificates in an array so that they can
9070-
# be used later (e.g., to check their expiration dates).
9071-
while true; do
9072-
[[ "$intermediates" =~ \-\-\-\-\-\BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break
9073-
intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}"
9074-
cert="${intermediates%%-----END CERTIFICATE-----*}"
9075-
intermediates="${intermediates#${cert}-----END CERTIFICATE-----}"
9076-
cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----"
9077-
9078-
# we count as humans in the file output here. This needs later to be adjusted in the code
9079-
fileout "intermediate_cert${json_postfix} $((certificates_provided + 1 ))" "INFO" "$cert"
9080-
9081-
fileout "intermediate_cert_fingerprintSHA256${json_postfix} $((certificates_provided + 1 ))" "INFO" "$(determine_cert_fingerprint_serial "$cert" "-fingerprint -sha256")"
9082-
9083-
intermediate_certs_txt[certificates_provided]="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert")"
9084-
9085-
# We don't need every value here. For the sake of consistency we add the rest
9086-
IFS=',' read -r startdate enddate diffseconds days2expire yearstart < <(determine_dates_certificate "${intermediate_certs_txt[certificates_provided]}")
9087-
fileout "intermediate_cert_notBefore${json_postfix} $((certificates_provided + 1))" "INFO" "$startdate"
9088-
expok="OK" #FIXME!
9089-
fileout "intermediate_cert_notAfter${json_postfix} $((certificates_provided + 1))" "$expok" "$enddate"
9090-
certificates_provided+=1
9091-
done
9092-
9093-
# courtesy Hanno Boeck (see https://github.com/hannob/badocspcert)
9094-
out "$indent"; pr_bold " Bad OCSP intermediate"
9095-
out " (exp.) "
9096-
jsonID="cert_bad_ocsp"
9097-
9098-
certificates_provided+=1
9099-
for (( i=0; i < certificates_provided-1; i++ )); do
9100-
cert_ext_keyusage="$(awk '/X509v3 Extended Key Usage:/ { getline; print $0 }' <<< "${intermediate_certs_txt[i]}")"
9101-
[[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break
9102-
done
9103-
9104-
#FIXME: We only raise the flag saying the chain is bad w/o naming the intermediate cert to blame.
9105-
if [[ $badocsp -eq 0 ]]; then
9106-
prln_svrty_medium "NOT ok"
9107-
fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is/are intermediate certificate(s)"
9108-
else
9109-
prln_svrty_good "Ok"
9110-
fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok"
9111-
fi
9112-
91139065
out "$indent"; pr_bold " ETS/\"eTLS\""
91149066
out ", visibility info "
91159067
jsonID="cert_eTLS"
@@ -9217,7 +9169,10 @@ certificate_info() {
92179169
fileout "cert_validityPeriod${json_postfix}" "INFO" "No finding"
92189170
fi
92199171

9220-
out "$indent"; pr_bold " # of certificates provided"; out " $certificates_provided"
9172+
out "$indent"; pr_bold " Certificates provided"
9173+
certificates_provided="$(grep -ac '\-\-\-\-\-BEGIN\ CERTIFICATE\-\-\-\-\-' <<< "$intermediates")"
9174+
((certificates_provided++)) # plus host certificate
9175+
out " $certificates_provided"
92219176
fileout "certs_countServer${json_postfix}" "INFO" "${certificates_provided}"
92229177
if "$certificate_list_ordering_problem"; then
92239178
prln_svrty_low " (certificate list ordering problem)"
@@ -9281,7 +9236,7 @@ certificate_info() {
92819236
else
92829237
if [[ $(count_lines "$ocsp_uri") -eq 1 ]]; then
92839238
out "$ocsp_uri"
9284-
if [[ "$expfinding" != "expired" ]]; then
9239+
if [[ "$expfinding" != expired ]]; then
92859240
check_revocation_ocsp "$ocsp_uri" "" "cert_ocspRevoked${json_postfix}"
92869241
fi
92879242
ret=$((ret +$?))
@@ -9295,7 +9250,7 @@ certificate_info() {
92959250
out "$spaces"
92969251
fi
92979252
out "$line"
9298-
if [[ "$expfinding" != "expired" ]]; then
9253+
if [[ "$expfinding" != expired ]]; then
92999254
check_revocation_ocsp "$line" "" "cert_ocspRevoked${json_postfix}"
93009255
ret=$((ret +$?))
93019256
fi
@@ -9339,7 +9294,7 @@ certificate_info() {
93399294
else
93409295
out "(response status unknown)"
93419296
fileout "${jsonID}${json_postfix}" "OK" " not sure what's going on here, '$ocsp_response'"
9342-
debugme grep -a -A20 -B2 "OCSP response" <<<"$ocsp_response"
9297+
debugme grep -a -A20 -B2 "OCSP response" <<< "$ocsp_response"
93439298
((ret++))
93449299
fi
93459300
fi
@@ -9391,6 +9346,79 @@ certificate_info() {
93919346
outln "$ct"
93929347
fileout "${jsonID}${json_postfix}" "INFO" "$ct"
93939348
fi
9349+
9350+
# Now we take care of the intermediate certificates. We basically (should) have them on disk
9351+
# as "intermediatecerts.pem" (which could be split into intermediatecert1.crt, intermediatecert2.crt, ..)
9352+
# However we do this in RAM which is better as it was passed to this function.
9353+
# We should keep in mind though this is somewhat redundant code. We do similar stuff elsewhere,
9354+
# e.g. in extract_certificates() and run_hpkp() but don't keep the certificates
9355+
9356+
#FIXME: output
9357+
# intermediate CN / (what about issuer. moving it?)
9358+
# fix the numbering schem of certificates_provided below
9359+
9360+
# Store all of the text output of the intermediate certificates in an array so that they can
9361+
# be used later (e.g., to check their expiration dates).
9362+
certificates_provided=0
9363+
while true; do
9364+
[[ "$intermediates" =~ \-\-\-\-\-BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break
9365+
intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}"
9366+
cert="${intermediates%%-----END CERTIFICATE-----*}"
9367+
intermediates="${intermediates#${cert}-----END CERTIFICATE-----}"
9368+
cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----"
9369+
9370+
# we count as humans in the file output here. This needs later to be adjusted in the code
9371+
fileout "intermediate_cert <#$((certificates_provided + 1))>${json_postfix}" "INFO" "$cert"
9372+
9373+
fileout "intermediate_cert_fingerprintSHA256 <#$((certificates_provided + 1))>${json_postfix}" "INFO" "$(determine_cert_fingerprint_serial "$cert" "-fingerprint -sha256")"
9374+
9375+
intermediate_certs_txt[certificates_provided]="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert")"
9376+
9377+
# We don't need every value here. For the sake of being consistent here we add the rest
9378+
IFS=',' read -r startdate enddate diffseconds days2expire yearstart < <(determine_dates_certificate "${intermediate_certs_txt[certificates_provided]}")
9379+
fileout "intermediate_cert_notBefore <#$((certificates_provided + 1))>${json_postfix}" "INFO" "$startdate"
9380+
9381+
if $first; then
9382+
out "$indent"; pr_bold " Intermediate cert validity "
9383+
first=false
9384+
else
9385+
out "$indent$spaces"
9386+
fi
9387+
if ! $OPENSSL x509 -checkend $((24*3600*20)) 2>>$ERRFILE <<< "$cert" | grep -qw not; then
9388+
out "#$((certificates_provided+1)): less then "; pr_svrty_high "20 days"
9389+
outln " at $enddate"
9390+
expok="HIGH"
9391+
elif ! $OPENSSL x509 -checkend $((24*3600*40)) 2>>$ERRFILE <<< "$cert" | grep -qw not; then
9392+
out "#$((certificates_provided+1)): less then "; pr_svrty_medium "40 days"
9393+
outln " at $enddate"
9394+
expok="MEDIUM"
9395+
else
9396+
outln "#$((certificates_provided+1)): longer than 40 days ($enddate)"
9397+
fi
9398+
fileout "intermediate_cert_notAfter <#$((certificates_provided + 1))>${json_postfix}" "$expok" "$enddate"
9399+
certificates_provided+=1
9400+
done
9401+
9402+
# Courtesy Hanno Böck (see https://github.com/hannob/badocspcert)
9403+
out "$indent"; pr_bold " Intermediate Bad OCSP"
9404+
out " (exp.) "
9405+
jsonID="intermediate_cert_badOCSP"
9406+
9407+
certificates_provided+=1
9408+
for (( i=0; i < certificates_provided-1; i++ )); do
9409+
cert_ext_keyusage="$(awk '/X509v3 Extended Key Usage:/ { getline; print $0 }' <<< "${intermediate_certs_txt[i]}")"
9410+
[[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break
9411+
done
9412+
9413+
#FIXME: We only raise the flag saying the chain is bad w/o naming the intermediate cert to blame.
9414+
if [[ $badocsp -eq 0 ]]; then
9415+
prln_svrty_medium "NOT ok"
9416+
fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is/are intermediate certificate(s)"
9417+
else
9418+
prln_svrty_good "Ok"
9419+
fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok"
9420+
fi
9421+
93949422
outln
93959423
return $ret
93969424
}

0 commit comments

Comments
 (0)