mirror of
https://github.com/drwetter/testssl.sh.git
synced 2025-01-01 06:19:44 +01:00
Merge pull request #1741 from drwetter/intermediate_cert_improvements
Intermediate cert improvements
This commit is contained in:
commit
b4c9437e95
@ -6,6 +6,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
use Text::Diff;
|
||||||
|
|
||||||
my $tests = 0;
|
my $tests = 0;
|
||||||
my $prg="./testssl.sh";
|
my $prg="./testssl.sh";
|
||||||
@ -15,7 +16,7 @@ my $html="";
|
|||||||
my $debughtml="";
|
my $debughtml="";
|
||||||
my $edited_html="";
|
my $edited_html="";
|
||||||
my $check2run="--ip=one --color 0 --htmlfile tmp.html";
|
my $check2run="--ip=one --color 0 --htmlfile tmp.html";
|
||||||
|
my $diff="";
|
||||||
die "Unable to open $prg" unless -f $prg;
|
die "Unable to open $prg" unless -f $prg;
|
||||||
|
|
||||||
printf "\n%s\n", "Doing HTML output checks";
|
printf "\n%s\n", "Doing HTML output checks";
|
||||||
@ -47,6 +48,9 @@ $edited_html =~ s/'/'/g;
|
|||||||
cmp_ok($edited_html, "eq", $out, "HTML file matches terminal output");
|
cmp_ok($edited_html, "eq", $out, "HTML file matches terminal output");
|
||||||
$tests++;
|
$tests++;
|
||||||
|
|
||||||
|
$diff = diff \$edited_html, \$out;
|
||||||
|
printf "\n%s\n", "$diff";
|
||||||
|
|
||||||
#2
|
#2
|
||||||
printf "\n%s\n", " .. running again $prg against \"$uri\", now with --debug 4 to create HTML output (may take another ~2 minutes)";
|
printf "\n%s\n", " .. running again $prg against \"$uri\", now with --debug 4 to create HTML output (may take another ~2 minutes)";
|
||||||
# Redirect stderr to /dev/null in order to avoid some unexplained "date: invalid date" error messages
|
# Redirect stderr to /dev/null in order to avoid some unexplained "date: invalid date" error messages
|
||||||
@ -72,5 +76,10 @@ $debughtml =~ s/.*DEBUG:.*\n//g;
|
|||||||
cmp_ok($debughtml, "eq", $html, "HTML file created with --debug 4 matches HTML file created without --debug");
|
cmp_ok($debughtml, "eq", $html, "HTML file created with --debug 4 matches HTML file created without --debug");
|
||||||
$tests++;
|
$tests++;
|
||||||
|
|
||||||
|
$diff = diff \$debughtml, \$html;
|
||||||
|
printf "\n%s\n", "$diff";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
printf "\n";
|
printf "\n";
|
||||||
done_testing($tests);
|
done_testing($tests);
|
||||||
|
472
testssl.sh
472
testssl.sh
@ -1992,10 +1992,10 @@ elif "$HAS_FREEBSDDATE"; then # FreeBSD, OS X and newer (~6.6) OpenBSD vers
|
|||||||
LC_ALL=C TZ=GMT date -j -f "$3" "$2" "$1"
|
LC_ALL=C TZ=GMT date -j -f "$3" "$2" "$1"
|
||||||
}
|
}
|
||||||
elif "$HAS_OPENBSDDATE"; then
|
elif "$HAS_OPENBSDDATE"; then
|
||||||
# We bascially echo it as a conversion as we want it is too difficult. Approach for that would be:
|
# We bascially echo it as a conversion as we want it is too difficult. Approach for that would be:
|
||||||
# printf '%s\n' "$1" | awk '{ printf "%04d%02d%02d\n", $4, $2, (index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3}'
|
# printf '%s\n' "$1" | awk '{ printf "%04d%02d%02d\n", $4, $2, (index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3}'
|
||||||
# 4: year, 1: month, 2: day, $3: time (e.g. "Dec 8 10:16:13 2016")
|
# 4: year, 1: month, 2: day, $3: time (e.g. "Dec 8 10:16:13 2016")
|
||||||
# This way we could also kind of convert args to epoch but as newer OpenBSDs "date" behave like FreeBSD
|
# This way we could also kind of convert args to epoch but as newer OpenBSDs "date" behave like FreeBSD
|
||||||
parse_date() {
|
parse_date() {
|
||||||
local tmp=""
|
local tmp=""
|
||||||
if [[ $2 == +%s* ]]; then
|
if [[ $2 == +%s* ]]; then
|
||||||
@ -2011,8 +2011,9 @@ else
|
|||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# arg1: An ASCII-HEX string
|
|
||||||
# Print $arg1 in binary format
|
# Print $arg1 in binary format. arg1: An ASCII-HEX string
|
||||||
|
#
|
||||||
asciihex_to_binary() {
|
asciihex_to_binary() {
|
||||||
local string="$1"
|
local string="$1"
|
||||||
local -i len
|
local -i len
|
||||||
@ -3567,7 +3568,7 @@ run_cipher_match(){
|
|||||||
hexc="${TLS_CIPHER_HEXCODE[i]}"
|
hexc="${TLS_CIPHER_HEXCODE[i]}"
|
||||||
if [[ ${#hexc} -eq 9 ]]; then
|
if [[ ${#hexc} -eq 9 ]]; then
|
||||||
hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}"
|
hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}"
|
||||||
if [[ "${hexc:2:2}" == "00" ]]; then
|
if [[ "${hexc:2:2}" == 00 ]]; then
|
||||||
normalized_hexcode[nr_ciphers]="x${hexc:7:2}"
|
normalized_hexcode[nr_ciphers]="x${hexc:7:2}"
|
||||||
else
|
else
|
||||||
normalized_hexcode[nr_ciphers]="x${hexc:2:2}${hexc:7:2}"
|
normalized_hexcode[nr_ciphers]="x${hexc:2:2}${hexc:7:2}"
|
||||||
@ -6468,9 +6469,8 @@ run_server_preference() {
|
|||||||
local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto=""
|
local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto=""
|
||||||
local default_cipher=""
|
local default_cipher=""
|
||||||
local limitedsense="" supported_sslv2_ciphers
|
local limitedsense="" supported_sslv2_ciphers
|
||||||
local -a offered_cipher offered_proto
|
|
||||||
local proto_ossl proto_txt proto_hex cipherlist i
|
local proto_ossl proto_txt proto_hex cipherlist i
|
||||||
local -i ret=0 j sclient_success str_len
|
local -i ret=0 j sclient_success
|
||||||
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"
|
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"
|
||||||
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"
|
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"
|
||||||
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,
|
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,
|
||||||
@ -8044,7 +8044,7 @@ etsi_ets_visibility_info() {
|
|||||||
# external functions to obtain the DER encoded certficate.
|
# external functions to obtain the DER encoded certficate.
|
||||||
if [[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\<unsupported\> ]] || \
|
if [[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\<unsupported\> ]] || \
|
||||||
[[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\ 0.4.0.3523.3.1 ]]; then
|
[[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\ 0.4.0.3523.3.1 ]]; then
|
||||||
dercert="$($OPENSSL x509 -in "$cert" -outform DER 2>>$ERRFILE | hexdump -v -e '16/1 "%02X"')"
|
dercert="$($OPENSSL x509 -outform DER 2>>$ERRFILE <<< "$cert" | hexdump -v -e '16/1 "%02X"')"
|
||||||
if [[ "$dercert" =~ 0603551D110101FF04[0-9A-F]*060604009B430301 ]] || \
|
if [[ "$dercert" =~ 0603551D110101FF04[0-9A-F]*060604009B430301 ]] || \
|
||||||
[[ "$dercert" =~ 0603551D1104[0-9A-F]*060604009B430301 ]]; then
|
[[ "$dercert" =~ 0603551D1104[0-9A-F]*060604009B430301 ]]; then
|
||||||
# Look for the beginning of the subjectAltName extension. It
|
# Look for the beginning of the subjectAltName extension. It
|
||||||
@ -8317,6 +8317,75 @@ certificate_transparency() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# replacement for inline $OPENSSL x509 -noout -in $HOSTCERT -serial
|
||||||
|
# and $OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha256/-sha1
|
||||||
|
#
|
||||||
|
determine_cert_fingerprint_serial() {
|
||||||
|
local cert="$1"
|
||||||
|
local ossl_command="$2"
|
||||||
|
local result=""
|
||||||
|
|
||||||
|
result="$($OPENSSL x509 -noout $ossl_command 2>>$ERRFILE <<< "$cert")"
|
||||||
|
# remove strings in text output, colon only appear in fingerprints
|
||||||
|
result="${result//Fingerprint=}"
|
||||||
|
result="${result//serial=}"
|
||||||
|
result="${result//:/}"
|
||||||
|
result="${result//SHA1 /}"
|
||||||
|
result="${result//SHA256 /}"
|
||||||
|
safe_echo "$result"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns startdate, enddate, diffseconds, days2expire as CSVs as strings
|
||||||
|
# arg1: human readable text string for certificate (openssl x509 -text -noout)
|
||||||
|
#
|
||||||
|
determine_dates_certificate() {
|
||||||
|
local cert_txt="$1"
|
||||||
|
local startdate enddate yearnow y m d yearstart clockstart yearend clockend
|
||||||
|
local diffseconds=0 days2expire=0
|
||||||
|
local -i secsaday=86400
|
||||||
|
|
||||||
|
startdate="${cert_txt#*Validity*Not Before: }"
|
||||||
|
# FreeBSD + OSX can't swallow the leading blank:
|
||||||
|
startdate="${startdate%%GMT*}GMT"
|
||||||
|
enddate="${cert_txt#*Validity*Not Before: *Not After : }"
|
||||||
|
enddate="${enddate%%GMT*}GMT"
|
||||||
|
# Now we have a normalized enddate and startdate like "Feb 27 10:03:20 2017 GMT" -- also for OpenBSD
|
||||||
|
if "$HAS_OPENBSDDATE"; then
|
||||||
|
# Best we want to do under old versions of OpenBSD, first just remove the GMT and keep start/endate for later output
|
||||||
|
startdate="$(parse_date "$startdate" "+%s")"
|
||||||
|
enddate="$(parse_date "$enddate" "+%s")"
|
||||||
|
# Now we extract a date block and a time block which we need for later output
|
||||||
|
startdate="$(parse_date "$startdate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
||||||
|
enddate="$(parse_date "$enddate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
||||||
|
read -r yearstart clockstart <<< "$startdate"
|
||||||
|
read -r yearend clockend <<< "$enddate"
|
||||||
|
debugme echo "$yearstart, $clockstart"
|
||||||
|
debugme echo "$yearend, $clockend"
|
||||||
|
y=$(( ${yearend:0:4} - ${yearstart:0:4} ))
|
||||||
|
m=$(( ${yearend:5:1} - ${yearstart:5:1} + ${yearend:6:1} - ${yearstart:6:1} ))
|
||||||
|
d=$(( ${yearend:8:2} - ${yearstart:8:2} ))
|
||||||
|
# We take the year, month, days here as old OpenBSD's date is too difficult for real conversion
|
||||||
|
# see comment in parse_date(). In diffseconds then we have the estimated absolute validity period
|
||||||
|
diffseconds=$(( d + ((m*30)) + ((y*365)) ))
|
||||||
|
diffseconds=$((diffseconds * secsaday))
|
||||||
|
# Now we estimate the days left plus length of month/year:
|
||||||
|
yearnow="$(date -juz GMT "+%Y-%m-%d %H:%M")"
|
||||||
|
y=$(( ${yearend:0:4} - ${yearnow:0:4} ))
|
||||||
|
m=$(( ${yearend:5:1} - ${yearnow:5:1} + ${yearend:6:1} - ${yearnow:6:1} ))
|
||||||
|
d=$(( ${yearend:8:2} - ${yearnow:8:2} ))
|
||||||
|
days2expire=$(( d + ((m*30)) + ((y*365)) ))
|
||||||
|
else
|
||||||
|
startdate="$(parse_date "$startdate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
||||||
|
enddate="$(parse_date "$enddate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
||||||
|
days2expire=$(( $(parse_date "$enddate" "+%s" $'%F %H:%M') - $(LC_ALL=C date "+%s") )) # first in seconds
|
||||||
|
days2expire=$((days2expire / secsaday))
|
||||||
|
diffseconds=$(( $(parse_date "$enddate" "+%s" $'%F %H:%M') - $(parse_date "$startdate" "+%s" $'%F %H:%M') ))
|
||||||
|
fi
|
||||||
|
safe_echo "$startdate,$enddate,$diffseconds,$days2expire,$yearstart"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
certificate_info() {
|
certificate_info() {
|
||||||
local proto
|
local proto
|
||||||
local -i certificate_number=$1
|
local -i certificate_number=$1
|
||||||
@ -8333,22 +8402,23 @@ certificate_info() {
|
|||||||
local ct="${12}"
|
local ct="${12}"
|
||||||
local certificate_list_ordering_problem="${13}"
|
local certificate_list_ordering_problem="${13}"
|
||||||
local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info
|
local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info
|
||||||
|
local hostcert=""
|
||||||
local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt"
|
local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt"
|
||||||
local -i lineno_matched=0
|
local -i lineno_matched=0
|
||||||
local cert_keyusage cert_ext_keyusage short_keyAlgo
|
local cert_keyusage cert_ext_keyusage short_keyAlgo
|
||||||
local outok=true
|
local outok=true
|
||||||
local expire days2expire secs2warn ocsp_uri crl
|
local days2expire secs2warn ocsp_uri crl
|
||||||
local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn
|
local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn
|
||||||
local issuer_DC issuerfinding cn_nosni=""
|
local issuer_DC issuerfinding cn_nosni=""
|
||||||
local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial cert
|
local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial cert
|
||||||
local -a intermediate_certs=()
|
local -a intermediate_certs_txt=()
|
||||||
local policy_oid
|
local policy_oid
|
||||||
local spaces=""
|
local spaces=""
|
||||||
local -i trust_sni=0 trust_nosni=0 diffseconds=0
|
local -i trust_sni=0 trust_nosni=0 diffseconds=0
|
||||||
local has_dns_sans has_dns_sans_nosni
|
local has_dns_sans has_dns_sans_nosni
|
||||||
local trust_sni_finding
|
local trust_sni_finding
|
||||||
local -i i certificates_provided=0
|
local -i i certificates_provided=0
|
||||||
local cnfinding trustfinding trustfinding_nosni
|
local cn_finding trustfinding trustfinding_nosni
|
||||||
local cnok="OK"
|
local cnok="OK"
|
||||||
local expfinding expok="OK"
|
local expfinding expok="OK"
|
||||||
local -i ret=0
|
local -i ret=0
|
||||||
@ -8361,10 +8431,12 @@ certificate_info() {
|
|||||||
local provides_stapling=false
|
local provides_stapling=false
|
||||||
local caa_node="" all_caa="" caa_property_name="" caa_property_value=""
|
local caa_node="" all_caa="" caa_property_name="" caa_property_value=""
|
||||||
local response=""
|
local response=""
|
||||||
local yearstart yearend clockstart clockend y m d
|
local yearstart
|
||||||
local gt_398=false gt_398warn=false
|
local gt_398=false gt_398warn=false
|
||||||
local gt_825=false gt_825warn=false
|
local gt_825=false gt_825warn=false
|
||||||
local badocsp=1
|
local -i secsaday=86400
|
||||||
|
local first=true
|
||||||
|
local badocsp=1
|
||||||
|
|
||||||
if [[ $number_of_certificates -gt 1 ]]; then
|
if [[ $number_of_certificates -gt 1 ]]; then
|
||||||
[[ $certificate_number -eq 1 ]] && outln
|
[[ $certificate_number -eq 1 ]] && outln
|
||||||
@ -8373,7 +8445,7 @@ certificate_info() {
|
|||||||
pr_headline "Server Certificate #$certificate_number"
|
pr_headline "Server Certificate #$certificate_number"
|
||||||
[[ -z "$sni_used" ]] && pr_underline " (in response to request w/o SNI)"
|
[[ -z "$sni_used" ]] && pr_underline " (in response to request w/o SNI)"
|
||||||
outln
|
outln
|
||||||
json_postfix=" <cert#${certificate_number}>"
|
json_postfix=" <hostCert#${certificate_number}>"
|
||||||
spaces=" "
|
spaces=" "
|
||||||
else
|
else
|
||||||
spaces=" "
|
spaces=" "
|
||||||
@ -8528,7 +8600,7 @@ certificate_info() {
|
|||||||
*GOST*|*gost*) short_keyAlgo="GOST";;
|
*GOST*|*gost*) short_keyAlgo="GOST";;
|
||||||
*dh*|*DH*) short_keyAlgo="DH" ;;
|
*dh*|*DH*) short_keyAlgo="DH" ;;
|
||||||
*) pr_fixme "don't know $cert_key_algo "
|
*) pr_fixme "don't know $cert_key_algo "
|
||||||
let ret++ ;;
|
((ret++)) ;;
|
||||||
esac
|
esac
|
||||||
out "$short_keyAlgo "
|
out "$short_keyAlgo "
|
||||||
# https://tools.ietf.org/html/rfc4492, https://www.keylength.com/en/compare/
|
# https://tools.ietf.org/html/rfc4492, https://www.keylength.com/en/compare/
|
||||||
@ -8699,42 +8771,44 @@ certificate_info() {
|
|||||||
fileout "${jsonID}${json_postfix}" "INFO" "$cert_ext_keyusage"
|
fileout "${jsonID}${json_postfix}" "INFO" "$cert_ext_keyusage"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
hostcert="$(<$HOSTCERT)"
|
||||||
|
|
||||||
out "$indent"; pr_bold " Serial / Fingerprints "
|
out "$indent"; pr_bold " Serial / Fingerprints "
|
||||||
cert_serial="$($OPENSSL x509 -noout -in $HOSTCERT -serial 2>>$ERRFILE | sed 's/serial=//')"
|
cert_serial="$(determine_cert_fingerprint_serial "$hostcert" "-serial")"
|
||||||
fileout "cert_serialNumber${json_postfix}" "INFO" "$cert_serial"
|
fileout "cert_serialNumber${json_postfix}" "INFO" "$cert_serial"
|
||||||
|
|
||||||
cert_fingerprint_sha1="$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha1 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g')"
|
cert_fingerprint_sha1="$(determine_cert_fingerprint_serial "$hostcert" "-fingerprint -sha1")"
|
||||||
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1//SHA1 /}"
|
outln "$cert_serial / SHA1 $cert_fingerprint_sha1"
|
||||||
outln "$cert_serial / $cert_fingerprint_sha1"
|
fileout "cert_fingerprintSHA1${json_postfix}" "INFO" "${cert_fingerprint_sha1}"
|
||||||
|
|
||||||
cert_fingerprint_sha2="$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha256 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g' )"
|
cert_fingerprint_sha2="$(determine_cert_fingerprint_serial "$hostcert" "-fingerprint -sha256")"
|
||||||
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2//SHA256 /}"
|
fileout "cert_fingerprintSHA256${json_postfix}" "INFO" "${cert_fingerprint_sha2}"
|
||||||
outln "$spaces$cert_fingerprint_sha2"
|
outln "${spaces}SHA256 ${cert_fingerprint_sha2}"
|
||||||
|
|
||||||
# " " needs to be converted back to lf in JSON/CSV output
|
# " " needs to be converted back to lf in JSON/CSV output. watch out leading/ending line containting "CERTIFICATE"
|
||||||
fileout "cert${json_postfix}" "INFO" "$(< $HOSTCERT)"
|
fileout "cert${json_postfix}" "INFO" "$hostcert"
|
||||||
|
|
||||||
[[ -z $CERT_FINGERPRINT_SHA2 ]] && \
|
[[ -z $CERT_FINGERPRINT_SHA2 ]] && \
|
||||||
CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2" ||
|
CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2" ||
|
||||||
CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2 $CERT_FINGERPRINT_SHA2"
|
CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2 $CERT_FINGERPRINT_SHA2"
|
||||||
[[ -z $RSA_CERT_FINGERPRINT_SHA2 ]] && \
|
[[ -z $RSA_CERT_FINGERPRINT_SHA2 ]] && \
|
||||||
( [[ $cert_key_algo = *RSA* ]] || [[ $cert_key_algo = *rsa* ]] ) &&
|
( [[ $cert_key_algo =~ RSA ]] || [[ $cert_key_algo =~ rsa ]] ) &&
|
||||||
RSA_CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2"
|
RSA_CERT_FINGERPRINT_SHA2="$cert_fingerprint_sha2"
|
||||||
|
|
||||||
out "$indent"; pr_bold " Common Name (CN) "
|
out "$indent"; pr_bold " Common Name (CN) "
|
||||||
cnfinding="Common Name (CN) : "
|
cn_finding="Common Name (CN) : "
|
||||||
cn="$(get_cn_from_cert $HOSTCERT)"
|
cn="$(get_cn_from_cert $HOSTCERT)"
|
||||||
if [[ -n "$cn" ]]; then
|
if [[ -n "$cn" ]]; then
|
||||||
pr_italic "$cn"
|
pr_italic "$cn"
|
||||||
cnfinding="$cn"
|
cn_finding="$cn"
|
||||||
else
|
else
|
||||||
cn="no CN field in subject"
|
cn="no CN field in subject"
|
||||||
out "($cn)"
|
out "($cn)"
|
||||||
cnfinding="$cn"
|
cn_finding="$cn"
|
||||||
cnok="INFO"
|
cnok="INFO"
|
||||||
fi
|
fi
|
||||||
fileout "cert_commonName${json_postfix}" "$cnok" "$cnfinding"
|
fileout "cert_commonName${json_postfix}" "$cnok" "$cn_finding"
|
||||||
cnfinding=""
|
cn_finding=""
|
||||||
|
|
||||||
if [[ -n "$sni_used" ]]; then
|
if [[ -n "$sni_used" ]]; then
|
||||||
if grep -q "\-\-\-\-\-BEGIN" "$HOSTCERT.nosni"; then
|
if grep -q "\-\-\-\-\-BEGIN" "$HOSTCERT.nosni"; then
|
||||||
@ -8748,24 +8822,24 @@ certificate_info() {
|
|||||||
|
|
||||||
if [[ -z "$sni_used" ]] || [[ "$(toupper "$cn_nosni")" == "$(toupper "$cn")" ]]; then
|
if [[ -z "$sni_used" ]] || [[ "$(toupper "$cn_nosni")" == "$(toupper "$cn")" ]]; then
|
||||||
outln
|
outln
|
||||||
cnfinding="$cn"
|
cn_finding="$cn"
|
||||||
elif [[ -z "$cn_nosni" ]]; then
|
elif [[ -z "$cn_nosni" ]]; then
|
||||||
out " (request w/o SNI didn't succeed";
|
out " (request w/o SNI didn't succeed";
|
||||||
cnfinding+="request w/o SNI didn't succeed"
|
cn_finding+="request w/o SNI didn't succeed"
|
||||||
if [[ $cert_sig_algo =~ ecdsa ]]; then
|
if [[ $cert_sig_algo =~ ecdsa ]]; then
|
||||||
out ", usual for EC certificates"
|
out ", usual for EC certificates"
|
||||||
cnfinding+=", usual for EC certificates"
|
cn_finding+=", usual for EC certificates"
|
||||||
fi
|
fi
|
||||||
outln ")"
|
outln ")"
|
||||||
cnfinding+=""
|
cn_finding+=""
|
||||||
elif [[ "$cn_nosni" == *"no CN field"* ]]; then
|
elif [[ "$cn_nosni" == *"no CN field"* ]]; then
|
||||||
outln ", (request w/o SNI: $cn_nosni)"
|
outln ", (request w/o SNI: $cn_nosni)"
|
||||||
cnfinding="$cn_nosni"
|
cn_finding="$cn_nosni"
|
||||||
else
|
else
|
||||||
out " (CN in response to request w/o SNI: "; pr_italic "$cn_nosni"; outln ")"
|
out " (CN in response to request w/o SNI: "; pr_italic "$cn_nosni"; outln ")"
|
||||||
cnfinding="$cn_nosni"
|
cn_finding="$cn_nosni"
|
||||||
fi
|
fi
|
||||||
fileout "cert_commonName_wo_SNI${json_postfix}" "INFO" "$cnfinding"
|
fileout "cert_commonName_wo_SNI${json_postfix}" "INFO" "$cn_finding"
|
||||||
|
|
||||||
sans=$(grep -A2 "Subject Alternative Name" <<< "$cert_txt" | \
|
sans=$(grep -A2 "Subject Alternative Name" <<< "$cert_txt" | \
|
||||||
grep -E "DNS:|IP Address:|email:|URI:|DirName:|Registered ID:" | tr ',' '\n' | \
|
grep -E "DNS:|IP Address:|email:|URI:|DirName:|Registered ID:" | tr ',' '\n' | \
|
||||||
@ -8792,49 +8866,13 @@ certificate_info() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
out "$indent"; pr_bold " Issuer "
|
# Determine the issuer now as we need them for host certificate warning
|
||||||
jsonID="cert_caIssuers"
|
issuer="$($OPENSSL x509 -noout -issuer -nameopt multiline,-align,sname,-esc_msb,utf8,-space_eq 2>>$ERRFILE <<< "$hostcert")"
|
||||||
#FIXME: oid would be better maybe (see above)
|
|
||||||
issuer="$($OPENSSL x509 -in $HOSTCERT -noout -issuer -nameopt multiline,-align,sname,-esc_msb,utf8,-space_eq 2>>$ERRFILE)"
|
|
||||||
issuer_CN="$(awk -F'=' '/CN=/ { print $2 }' <<< "$issuer")"
|
issuer_CN="$(awk -F'=' '/CN=/ { print $2 }' <<< "$issuer")"
|
||||||
issuer_O="$(awk -F'=' '/O=/ { print $2 }' <<< "$issuer")"
|
issuer_O="$(awk -F'=' '/O=/ { print $2 }' <<< "$issuer")"
|
||||||
issuer_C="$(awk -F'=' '/ C=/ { print $2 }' <<< "$issuer")"
|
issuer_C="$(awk -F'=' '/ C=/ { print $2 }' <<< "$issuer")"
|
||||||
issuer_DC="$(awk -F'=' '/DC=/ { print $2 }' <<< "$issuer")"
|
issuer_DC="$(awk -F'=' '/DC=/ { print $2 }' <<< "$issuer")"
|
||||||
|
|
||||||
if [[ "$issuer_O" == "issuer=" ]] || [[ "$issuer_O" == "issuer= " ]] || [[ "$issuer_CN" == "$cn" ]]; then
|
|
||||||
prln_svrty_critical "self-signed (NOT ok)"
|
|
||||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "selfsigned"
|
|
||||||
set_grade_cap "T" "Self-signed certificate"
|
|
||||||
else
|
|
||||||
issuerfinding="$issuer_CN"
|
|
||||||
pr_italic "$issuer_CN"
|
|
||||||
if [[ -z "$issuer_O" ]] && [[ -n "$issuer_DC" ]]; then
|
|
||||||
for san in $issuer_DC; do
|
|
||||||
if [[ -z "$issuer_O" ]]; then
|
|
||||||
issuer_O="${san}"
|
|
||||||
else
|
|
||||||
issuer_O="${san}.${issuer_O}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
if [[ -n "$issuer_O" ]]; then
|
|
||||||
issuerfinding+=" ("
|
|
||||||
out " ("
|
|
||||||
issuerfinding+="$issuer_O"
|
|
||||||
pr_italic "$issuer_O"
|
|
||||||
if [[ -n "$issuer_C" ]]; then
|
|
||||||
issuerfinding+=" from "
|
|
||||||
out " from "
|
|
||||||
issuerfinding+="$issuer_C"
|
|
||||||
pr_italic "$issuer_C"
|
|
||||||
fi
|
|
||||||
issuerfinding+=")"
|
|
||||||
out ")"
|
|
||||||
fi
|
|
||||||
outln
|
|
||||||
fileout "${jsonID}${json_postfix}" "INFO" "$issuerfinding"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out "$indent"; pr_bold " Trust (hostname) "
|
out "$indent"; pr_bold " Trust (hostname) "
|
||||||
compare_server_name_to_cert "$HOSTCERT"
|
compare_server_name_to_cert "$HOSTCERT"
|
||||||
trust_sni=$?
|
trust_sni=$?
|
||||||
@ -8980,7 +9018,7 @@ certificate_info() {
|
|||||||
out "no "
|
out "no "
|
||||||
fileout "${jsonID}${json_postfix}" "INFO" "no"
|
fileout "${jsonID}${json_postfix}" "INFO" "no"
|
||||||
fi
|
fi
|
||||||
debugme echo "($(newline_to_spaces "$policy_oid"))"
|
debugme1 echo -n "($(newline_to_spaces "$policy_oid"))"
|
||||||
outln
|
outln
|
||||||
#TODO: check browser OIDs:
|
#TODO: check browser OIDs:
|
||||||
# https://dxr.mozilla.org/mozilla-central/source/security/certverifier/ExtendedValidation.cpp
|
# https://dxr.mozilla.org/mozilla-central/source/security/certverifier/ExtendedValidation.cpp
|
||||||
@ -8988,90 +9026,9 @@ certificate_info() {
|
|||||||
# https://certs.opera.com/03/ev-oids.xml
|
# https://certs.opera.com/03/ev-oids.xml
|
||||||
# see #967
|
# see #967
|
||||||
|
|
||||||
# courtesy Hanno Boeck (see https://github.com/hannob/badocspcert)
|
|
||||||
out "$indent"; pr_bold " Bad OCSP intermediate"
|
|
||||||
out " (exp.) "
|
|
||||||
jsonID="cert_bad_ocsp"
|
|
||||||
|
|
||||||
# There might be >1 certificate, so we split intermediatecerts.pem e.g. into
|
|
||||||
# intermediatecert1.crt, intermediatecert2.cert.
|
|
||||||
#FIXME: This is redundant code. We do that elsewhere, e.g. before in extract_certificates()
|
|
||||||
# and run_hpkp() at least but didn't keep the result
|
|
||||||
#
|
|
||||||
#FIXME: We just raise the flag saying the chain is bad w/o naming the intermediate
|
|
||||||
# cert to blame.
|
|
||||||
|
|
||||||
# Store all of the intermediate certificates in an array so that they can
|
|
||||||
# be used later (e.g., to check their expiration dates).
|
|
||||||
while true; do
|
|
||||||
[[ "$intermediates" =~ \-\-\-\-\-\BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break
|
|
||||||
intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}"
|
|
||||||
cert="${intermediates%%-----END CERTIFICATE-----*}"
|
|
||||||
intermediates="${intermediates#${cert}-----END CERTIFICATE-----}"
|
|
||||||
cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----"
|
|
||||||
intermediate_certs[certificates_provided]="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert")"
|
|
||||||
certificates_provided+=1
|
|
||||||
done
|
|
||||||
certificates_provided+=1
|
|
||||||
for (( i=0; i < certificates_provided-1; i++ )); do
|
|
||||||
cert_ext_keyusage="$(awk '/X509v3 Extended Key Usage:/ { getline; print $0 }' <<< "${intermediate_certs[i]}")"
|
|
||||||
[[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break
|
|
||||||
done
|
|
||||||
if [[ $badocsp -eq 0 ]]; then
|
|
||||||
prln_svrty_medium "NOT ok"
|
|
||||||
fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is/are intermediate certificate(s)"
|
|
||||||
else
|
|
||||||
prln_svrty_good "Ok"
|
|
||||||
fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok"
|
|
||||||
fi
|
|
||||||
|
|
||||||
out "$indent"; pr_bold " ETS/\"eTLS\""
|
|
||||||
out ", visibility info "
|
|
||||||
jsonID="cert_eTLS"
|
|
||||||
etsi_ets_visibility_info "${jsonID}${json_postfix}" "$spaces" "$HOSTCERT" "$cert_txt"
|
|
||||||
# *Currently* this is even listed as a vulnerability (CWE-310, CVE-2019-919), see
|
|
||||||
# https://nvd.nist.gov/vuln/detail/CVE-2019-9191, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9191
|
|
||||||
# For now we leave this here. We may want to change that later or add infos to other sections (FS & vulnerability)
|
|
||||||
|
|
||||||
out "$indent"; pr_bold " Certificate Validity (UTC) "
|
out "$indent"; pr_bold " Certificate Validity (UTC) "
|
||||||
# FreeBSD + OSX can't swallow the leading blank:
|
IFS=',' read -r startdate enddate diffseconds days2expire yearstart < <(determine_dates_certificate "$cert_txt")
|
||||||
startdate="${cert_txt#*Validity*Not Before: }"
|
|
||||||
startdate="${startdate%%GMT*}GMT"
|
|
||||||
enddate="${cert_txt#*Validity*Not Before: *Not After : }"
|
|
||||||
enddate="${enddate%%GMT*}GMT"
|
|
||||||
debugme echo "$enddate - $startdate"
|
|
||||||
# Now we have a normalized enddate and startdate like "Feb 27 10:03:20 2017 GMT" -- also for OpenBSD
|
|
||||||
if "$HAS_OPENBSDDATE"; then
|
|
||||||
# Best we want to do under old versions of OpenBSD, first just remove the GMT and keep start/endate for later output
|
|
||||||
startdate="$(parse_date "$startdate" "+%s")"
|
|
||||||
enddate="$(parse_date "$enddate" "+%s")"
|
|
||||||
# Now we extract a date block and a time block which we need for later output
|
|
||||||
startdate="$(parse_date "$startdate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
|
||||||
enddate="$(parse_date "$enddate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
|
||||||
read yearstart clockstart <<< "$startdate"
|
|
||||||
read yearend clockend <<< "$enddate"
|
|
||||||
debugme echo "$yearstart, $clockstart"
|
|
||||||
debugme echo "$yearend, $clockend"
|
|
||||||
y=$(( ${yearend:0:4} - ${yearstart:0:4} ))
|
|
||||||
m=$(( ${yearend:5:1} - ${yearstart:5:1} + ${yearend:6:1} - ${yearstart:6:1} ))
|
|
||||||
d=$(( ${yearend:8:2} - ${yearstart:8:2} ))
|
|
||||||
# We take the year, month, days here as old OpenBSD's date is too difficult for real conversion
|
|
||||||
# see comment in parse_date(). In diffseconds then we have the estimated absolute validity period
|
|
||||||
diffseconds=$(( d + ((m*30)) + ((y*365)) ))
|
|
||||||
diffseconds=$((diffseconds * 3600 * 24))
|
|
||||||
# Now we estimate the days left plus length of month/year:
|
|
||||||
yearnow="$(date -juz GMT "+%Y-%m-%d %H:%M")"
|
|
||||||
y=$(( ${yearend:0:4} - ${yearnow:0:4} ))
|
|
||||||
m=$(( ${yearend:5:1} - ${yearnow:5:1} + ${yearend:6:1} - ${yearnow:6:1} ))
|
|
||||||
d=$(( ${yearend:8:2} - ${yearnow:8:2} ))
|
|
||||||
days2expire=$(( d + ((m*30)) + ((y*365)) ))
|
|
||||||
else
|
|
||||||
startdate="$(parse_date "$startdate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
|
||||||
enddate="$(parse_date "$enddate" +"%F %H:%M" "%b %d %T %Y %Z")"
|
|
||||||
days2expire=$(( $(parse_date "$enddate" "+%s" $'%F %H:%M') - $(LC_ALL=C date "+%s") )) # first in seconds
|
|
||||||
days2expire=$((days2expire / 3600 / 24 ))
|
|
||||||
diffseconds=$(( $(parse_date "$enddate" "+%s" $'%F %H:%M') - $(parse_date "$startdate" "+%s" $'%F %H:%M') ))
|
|
||||||
fi
|
|
||||||
# We adjust the thresholds by %50 for LE certificates, relaxing warnings for those certificates.
|
# We adjust the thresholds by %50 for LE certificates, relaxing warnings for those certificates.
|
||||||
# . instead of \' because it does not break syntax highlighting in vim
|
# . instead of \' because it does not break syntax highlighting in vim
|
||||||
if [[ "$issuer_CN" =~ ^Let.s\ Encrypt\ Authority ]] ; then
|
if [[ "$issuer_CN" =~ ^Let.s\ Encrypt\ Authority ]] ; then
|
||||||
@ -9079,20 +9036,17 @@ certificate_info() {
|
|||||||
days2warn1=$((days2warn1 / 2))
|
days2warn1=$((days2warn1 / 2))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debugme echo -n "diffseconds: $diffseconds"
|
debugme echo -n "(diffseconds: $diffseconds)"
|
||||||
expire=$($OPENSSL x509 -in $HOSTCERT -checkend 1 2>>$ERRFILE)
|
if ! [[ "$($OPENSSL x509 -checkend 1 2>>$ERRFILE <<< "$hostcert")" =~ \ not\ ]]; then
|
||||||
if ! grep -qw not <<< "$expire" ; then
|
|
||||||
pr_svrty_critical "expired"
|
pr_svrty_critical "expired"
|
||||||
expfinding="expired"
|
expfinding="expired"
|
||||||
expok="CRITICAL"
|
expok="CRITICAL"
|
||||||
set_grade_cap "T" "Certificate expired"
|
set_grade_cap "T" "Certificate expired"
|
||||||
else
|
else
|
||||||
secs2warn=$((24 * 60 * 60 * days2warn2)) # low threshold first
|
# low threshold first
|
||||||
expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE)
|
if ! [[ "$($OPENSSL x509 -checkend $((secsaday*days2warn2)) 2>>$ERRFILE <<< "$cert")" =~ \ not\ ]]; then
|
||||||
if grep -qw not <<< "$expire"; then
|
# high threshold
|
||||||
secs2warn=$((24 * 60 * 60 * days2warn1)) # high threshold
|
if ! [[ "$($OPENSSL x509 -checkend $((secsaday*days2warn1)) 2>>$ERRFILE <<< "$cert")" =~ \ not\ ]]; then
|
||||||
expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE)
|
|
||||||
if grep -qw not <<< "$expire"; then
|
|
||||||
pr_svrty_good "$days2expire >= $days2warn1 days"
|
pr_svrty_good "$days2expire >= $days2warn1 days"
|
||||||
expfinding+="$days2expire >= $days2warn1 days"
|
expfinding+="$days2expire >= $days2warn1 days"
|
||||||
else
|
else
|
||||||
@ -9113,15 +9067,15 @@ certificate_info() {
|
|||||||
|
|
||||||
# Internal certificates or those from appliances often have too high validity periods.
|
# Internal certificates or those from appliances often have too high validity periods.
|
||||||
# We check for ~10 years and >~ 5 years
|
# We check for ~10 years and >~ 5 years
|
||||||
if [[ $diffseconds -ge $((3600 * 24 * 365 * 10)) ]]; then
|
if [[ $diffseconds -ge $((secsaday*365*10)) ]]; then
|
||||||
out "$spaces"
|
out "$spaces"
|
||||||
prln_svrty_high ">= 10 years is way too long"
|
prln_svrty_high ">= 10 years is way too long"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "HIGH" "$((diffseconds / (3600 * 24) )) days"
|
fileout "cert_extlifeSpan${json_postfix}" "HIGH" "$((diffseconds / secsaday)) days"
|
||||||
elif [[ $diffseconds -ge $((3600 * 24 * 365 * 5)) ]]; then
|
elif [[ $diffseconds -ge $((secsaday*365*5)) ]]; then
|
||||||
out "$spaces"
|
out "$spaces"
|
||||||
prln_svrty_medium ">= 5 years is too long"
|
prln_svrty_medium ">= 5 years is too long"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "MEDIUM" "$((diffseconds / (3600 * 24) )) days"
|
fileout "cert_extlifeSpan${json_postfix}" "MEDIUM" "$((diffseconds / secsaday)) days"
|
||||||
elif [[ $diffseconds -ge $((3600 * 24 * 398 + 1)) ]]; then
|
elif [[ $diffseconds -ge $((secsaday*398 + 1)) ]]; then
|
||||||
# Also "official" certificates issued from september 1st 2020 (1598918400) aren't supposed
|
# Also "official" certificates issued from september 1st 2020 (1598918400) aren't supposed
|
||||||
# to be valid longer than 398 days which is 34387200 in epoch seconds
|
# to be valid longer than 398 days which is 34387200 in epoch seconds
|
||||||
gt_398=true
|
gt_398=true
|
||||||
@ -9136,12 +9090,12 @@ certificate_info() {
|
|||||||
out "$spaces"
|
out "$spaces"
|
||||||
if "$gt_398warn" && "$gt_398"; then
|
if "$gt_398warn" && "$gt_398"; then
|
||||||
prln_svrty_medium "> 398 days issued after 2020/09/01 is too long"
|
prln_svrty_medium "> 398 days issued after 2020/09/01 is too long"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "MEDIUM" "$((diffseconds / (3600 * 24) )) > 398 days"
|
fileout "cert_extlifeSpan${json_postfix}" "MEDIUM" "$((diffseconds / secsaday)) > 398 days"
|
||||||
elif "$gt_398"; then
|
elif "$gt_398"; then
|
||||||
outln ">= 398 days certificate life time but issued before 2020/09/01"
|
outln ">= 398 days certificate life time but issued before 2020/09/01"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "INFO" "$((diffseconds / (3600 * 24) )) =< 398 days"
|
fileout "cert_extlifeSpan${json_postfix}" "INFO" "$((diffseconds / secsaday)) =< 398 days"
|
||||||
fi
|
fi
|
||||||
elif [[ $diffseconds -ge $((3600 * 24 * 825 + 1)) ]]; then
|
elif [[ $diffseconds -ge $((secsaday*825 + 1)) ]]; then
|
||||||
# Also "official" certificates issued from March 1st, 2018 (1517353200) aren't supposed
|
# Also "official" certificates issued from March 1st, 2018 (1517353200) aren't supposed
|
||||||
# to be valid longer than 825 days which is 1517353200 in epoch seconds
|
# to be valid longer than 825 days which is 1517353200 in epoch seconds
|
||||||
gt_825=true
|
gt_825=true
|
||||||
@ -9156,27 +9110,25 @@ certificate_info() {
|
|||||||
out "$spaces"
|
out "$spaces"
|
||||||
if "$gt_825warn" && "$gt_825"; then
|
if "$gt_825warn" && "$gt_825"; then
|
||||||
prln_svrty_medium "> 825 days issued after 2018/03/01 is too long"
|
prln_svrty_medium "> 825 days issued after 2018/03/01 is too long"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "MEDIUM" "$((diffseconds / (3600 * 24) )) > 825 days"
|
fileout "cert_extlifeSpan${json_postfix}" "MEDIUM" "$((diffseconds / secsaday)) > 825 days"
|
||||||
elif "$gt_825"; then
|
elif "$gt_825"; then
|
||||||
outln ">= 825 days certificate life time but issued before 2018/03/01"
|
outln ">= 825 days certificate life time but issued before 2018/03/01"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "INFO" "$((diffseconds / (3600 * 24) )) =< 825 days"
|
fileout "cert_extlifeSpan${json_postfix}" "INFO" "$((diffseconds / secsaday)) =< 825 days"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# All is fine with valididy period
|
# All is fine with validity period
|
||||||
# We ignore for now certificates < 2018/03/01. On the screen we only show debug info
|
# We ignore for now certificates < 2018/03/01. On the screen we only show debug info
|
||||||
[[ "$DEBUG" -ge 1 ]] && outln "${spaces}DEBUG: all is fine with total certificate life time"
|
debugme1 echo "${spaces}DEBUG: all is fine with total certificate life time"
|
||||||
fileout "cert_validityPeriod${json_postfix}" "INFO" "No finding"
|
fileout "cert_extlifeSpan${json_postfix}" "OK" "certificate has no extended life time according to browser forum"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
out "$indent"; pr_bold " # of certificates provided"; out " $certificates_provided"
|
out "$indent"; pr_bold " ETS/\"eTLS\""
|
||||||
fileout "certs_countServer${json_postfix}" "INFO" "${certificates_provided}"
|
out ", visibility info "
|
||||||
if "$certificate_list_ordering_problem"; then
|
jsonID="cert_eTLS"
|
||||||
prln_svrty_low " (certificate list ordering problem)"
|
etsi_ets_visibility_info "${jsonID}${json_postfix}" "$spaces" "$hostcert" "$cert_txt"
|
||||||
fileout "certs_list_ordering_problem${json_postfix}" "LOW" "yes"
|
# *Currently* this is even listed as a vulnerability (CWE-310, CVE-2019-919), see
|
||||||
else
|
# https://nvd.nist.gov/vuln/detail/CVE-2019-9191, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9191
|
||||||
fileout "certs_list_ordering_problem${json_postfix}" "INFO" "no"
|
# For now we leave this here. We may want to change that later or add infos to other sections (FS & vulnerability)
|
||||||
outln
|
|
||||||
fi
|
|
||||||
|
|
||||||
if "$PHONE_OUT"; then
|
if "$PHONE_OUT"; then
|
||||||
out "$indent"; pr_bold " In pwnedkeys.com DB "
|
out "$indent"; pr_bold " In pwnedkeys.com DB "
|
||||||
@ -9225,14 +9177,14 @@ certificate_info() {
|
|||||||
|
|
||||||
out "$indent"; pr_bold " OCSP URI "
|
out "$indent"; pr_bold " OCSP URI "
|
||||||
jsonID="cert_ocspURL"
|
jsonID="cert_ocspURL"
|
||||||
ocsp_uri=$($OPENSSL x509 -in $HOSTCERT -noout -ocsp_uri 2>>$ERRFILE)
|
ocsp_uri="$($OPENSSL x509 -noout -ocsp_uri 2>>$ERRFILE <<< "$hostcert")"
|
||||||
if [[ -z "$ocsp_uri" ]]; then
|
if [[ -z "$ocsp_uri" ]]; then
|
||||||
outln "--"
|
outln "--"
|
||||||
fileout "${jsonID}${json_postfix}" "INFO" "--"
|
fileout "${jsonID}${json_postfix}" "INFO" "--"
|
||||||
else
|
else
|
||||||
if [[ $(count_lines "$ocsp_uri") -eq 1 ]]; then
|
if [[ $(count_lines "$ocsp_uri") -eq 1 ]]; then
|
||||||
out "$ocsp_uri"
|
out "$ocsp_uri"
|
||||||
if [[ "$expfinding" != "expired" ]]; then
|
if [[ "$expfinding" != expired ]]; then
|
||||||
check_revocation_ocsp "$ocsp_uri" "" "cert_ocspRevoked${json_postfix}"
|
check_revocation_ocsp "$ocsp_uri" "" "cert_ocspRevoked${json_postfix}"
|
||||||
fi
|
fi
|
||||||
ret=$((ret +$?))
|
ret=$((ret +$?))
|
||||||
@ -9246,7 +9198,7 @@ certificate_info() {
|
|||||||
out "$spaces"
|
out "$spaces"
|
||||||
fi
|
fi
|
||||||
out "$line"
|
out "$line"
|
||||||
if [[ "$expfinding" != "expired" ]]; then
|
if [[ "$expfinding" != expired ]]; then
|
||||||
check_revocation_ocsp "$line" "" "cert_ocspRevoked${json_postfix}"
|
check_revocation_ocsp "$line" "" "cert_ocspRevoked${json_postfix}"
|
||||||
ret=$((ret +$?))
|
ret=$((ret +$?))
|
||||||
fi
|
fi
|
||||||
@ -9290,7 +9242,7 @@ certificate_info() {
|
|||||||
else
|
else
|
||||||
out "(response status unknown)"
|
out "(response status unknown)"
|
||||||
fileout "${jsonID}${json_postfix}" "OK" " not sure what's going on here, '$ocsp_response'"
|
fileout "${jsonID}${json_postfix}" "OK" " not sure what's going on here, '$ocsp_response'"
|
||||||
debugme grep -a -A20 -B2 "OCSP response" <<<"$ocsp_response"
|
debugme grep -a -A20 -B2 "OCSP response" <<< "$ocsp_response"
|
||||||
((ret++))
|
((ret++))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@ -9310,7 +9262,7 @@ certificate_info() {
|
|||||||
caa_node=${caa_node#*.}
|
caa_node=${caa_node#*.}
|
||||||
done
|
done
|
||||||
if [[ -n "$caa" ]]; then
|
if [[ -n "$caa" ]]; then
|
||||||
pr_svrty_good "available"; out " - please check for match with \"Issuer\" above"
|
pr_svrty_good "available"; out " - please check for match with \"Issuer\" below"
|
||||||
if [[ $(count_lines "$caa") -eq 1 ]]; then
|
if [[ $(count_lines "$caa") -eq 1 ]]; then
|
||||||
out ": "
|
out ": "
|
||||||
else
|
else
|
||||||
@ -9342,6 +9294,132 @@ certificate_info() {
|
|||||||
outln "$ct"
|
outln "$ct"
|
||||||
fileout "${jsonID}${json_postfix}" "INFO" "$ct"
|
fileout "${jsonID}${json_postfix}" "INFO" "$ct"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
out "$indent"; pr_bold " Certificates provided"
|
||||||
|
certificates_provided="$(grep -ac '\-\-\-\-\-BEGIN\ CERTIFICATE\-\-\-\-\-' <<< "$intermediates")"
|
||||||
|
((certificates_provided++)) # plus host certificate
|
||||||
|
out " $certificates_provided"
|
||||||
|
fileout "certs_countServer${json_postfix}" "INFO" "${certificates_provided}"
|
||||||
|
if "$certificate_list_ordering_problem"; then
|
||||||
|
prln_svrty_low " (certificate list ordering problem)"
|
||||||
|
fileout "certs_list_ordering_problem${json_postfix}" "LOW" "yes"
|
||||||
|
else
|
||||||
|
fileout "certs_list_ordering_problem${json_postfix}" "INFO" "no"
|
||||||
|
outln
|
||||||
|
fi
|
||||||
|
|
||||||
|
out "$indent"; pr_bold " Issuer "
|
||||||
|
jsonID="cert_caIssuers"
|
||||||
|
|
||||||
|
if [[ "$issuer_O" == issuer= ]] || [[ "$issuer_O" == issuer=\ ]] || [[ "$issuer_CN" == "$cn" ]]; then
|
||||||
|
prln_svrty_critical "self-signed (NOT ok)"
|
||||||
|
fileout "${jsonID}${json_postfix}" "CRITICAL" "selfsigned"
|
||||||
|
set_grade_cap "T" "Self-signed certificate"
|
||||||
|
else
|
||||||
|
issuerfinding="$issuer_CN"
|
||||||
|
pr_italic "$issuer_CN"
|
||||||
|
if [[ -z "$issuer_O" ]] && [[ -n "$issuer_DC" ]]; then
|
||||||
|
for san in $issuer_DC; do
|
||||||
|
if [[ -z "$issuer_O" ]]; then
|
||||||
|
issuer_O="${san}"
|
||||||
|
else
|
||||||
|
issuer_O="${san}.${issuer_O}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ -n "$issuer_O" ]]; then
|
||||||
|
issuerfinding+=" ("
|
||||||
|
out " ("
|
||||||
|
issuerfinding+="$issuer_O"
|
||||||
|
pr_italic "$issuer_O"
|
||||||
|
if [[ -n "$issuer_C" ]]; then
|
||||||
|
issuerfinding+=" from "
|
||||||
|
out " from "
|
||||||
|
issuerfinding+="$issuer_C"
|
||||||
|
pr_italic "$issuer_C"
|
||||||
|
fi
|
||||||
|
issuerfinding+=")"
|
||||||
|
out ")"
|
||||||
|
fi
|
||||||
|
outln
|
||||||
|
fileout "${jsonID}${json_postfix}" "INFO" "$issuerfinding"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Now we take care of the intermediate certificates. We basically (should) have them on disk
|
||||||
|
# as "intermediatecerts.pem" (which could be split into intermediatecert1.crt, intermediatecert2.crt, ..)
|
||||||
|
# However we do this in RAM which is better as it was passed to this function.
|
||||||
|
# We should keep in mind though this is somewhat redundant code. We do similar stuff elsewhere,
|
||||||
|
# e.g. in extract_certificates() and run_hpkp() but don't keep the certificates
|
||||||
|
|
||||||
|
# Store all of the text output of the intermediate certificates in an array so that they can
|
||||||
|
# be used later (e.g., to check their expiration dates).
|
||||||
|
for (( i=1; i < certificates_provided; i++ )); do
|
||||||
|
[[ "$intermediates" =~ \-\-\-\-\-BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break
|
||||||
|
intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}"
|
||||||
|
cert="${intermediates%%-----END CERTIFICATE-----*}"
|
||||||
|
intermediates="${intermediates#${cert}-----END CERTIFICATE-----}"
|
||||||
|
cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----"
|
||||||
|
|
||||||
|
fileout "intermediate_cert <#${i}>${json_postfix}" "INFO" "$cert"
|
||||||
|
fileout "intermediate_cert_fingerprintSHA256 <#${i}>${json_postfix}" "INFO" "$(determine_cert_fingerprint_serial "$cert" "-fingerprint -sha256")"
|
||||||
|
|
||||||
|
intermediate_certs_txt[i]="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert")"
|
||||||
|
|
||||||
|
# We don't need every value here. For the sake of being consistent here we add the rest
|
||||||
|
IFS=',' read -r startdate enddate diffseconds days2expire yearstart < <(determine_dates_certificate "${intermediate_certs_txt[i]}")
|
||||||
|
fileout "intermediate_cert_notBefore <#${i}>${json_postfix}" "INFO" "$startdate"
|
||||||
|
|
||||||
|
if $first; then
|
||||||
|
out "$indent"; pr_bold " Intermediate cert validity "
|
||||||
|
first=false
|
||||||
|
else
|
||||||
|
out "$indent$spaces"
|
||||||
|
fi
|
||||||
|
out "#${i}: "
|
||||||
|
if ! [[ "$($OPENSSL x509 -checkend 1 2>>$ERRFILE <<< "$cert")" =~ \ not\ ]]; then
|
||||||
|
cn_finding="expired!"
|
||||||
|
pr_svrty_critical "$cn_finding"
|
||||||
|
expok="CRITICAL"
|
||||||
|
elif ! [[ "$($OPENSSL x509 -checkend $((secsaday*20)) 2>>$ERRFILE <<< "$cert")" =~ \ not\ ]]; then
|
||||||
|
cn_finding="expires <= 20 days"
|
||||||
|
pr_svrty_high "$cn_finding"
|
||||||
|
expok="HIGH"
|
||||||
|
elif ! [[ "$($OPENSSL x509 -checkend $((secsaday*40)) 2>>$ERRFILE <<< "$cert")" =~ \ not\ ]]; then
|
||||||
|
cn_finding="expires <= 40 days"
|
||||||
|
pr_svrty_medium "$cn_finding"
|
||||||
|
expok="MEDIUM"
|
||||||
|
else
|
||||||
|
cn_finding="ok > 40 days"
|
||||||
|
pr_svrty_good "$cn_finding"
|
||||||
|
expok="OK"
|
||||||
|
fi
|
||||||
|
out " ($enddate). "
|
||||||
|
cn="$(awk -F= '/Subject:.*CN/ { print $NF }' <<< "${intermediate_certs_txt[i]}")"
|
||||||
|
issuer_CN="$(awk -F= '/Issuer:.*CN/ { print $NF }' <<< "${intermediate_certs_txt[i]}")"
|
||||||
|
pr_italic "$cn"; out " <-- "; prln_italic "$issuer_CN"
|
||||||
|
fileout "intermediate_cert_notAfter <#${i}>${json_postfix}" "$expok" "$enddate"
|
||||||
|
fileout "intermediate_cert_expiration <#${i}>${json_postfix}" "$expok" "$cn_finding"
|
||||||
|
fileout "intermediate_cert_chain <#${i}>${json_postfix}" "INFO" "$cn <-- $issuer_CN"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Courtesy Hanno Böck (see https://github.com/hannob/badocspcert)
|
||||||
|
out "$indent"; pr_bold " Intermediate Bad OCSP"
|
||||||
|
out " (exp.) "
|
||||||
|
jsonID="intermediate_cert_badOCSP"
|
||||||
|
|
||||||
|
for (( i=1; i < certificates_provided; i++ )); do
|
||||||
|
cert_ext_keyusage="$(awk '/X509v3 Extended Key Usage:/ { getline; print $0 }' <<< "${intermediate_certs_txt[i]}")"
|
||||||
|
[[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break
|
||||||
|
done
|
||||||
|
if [[ $badocsp -eq 0 ]]; then
|
||||||
|
prln_svrty_medium "NOT ok"
|
||||||
|
fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is intermediate certificate ${i}"
|
||||||
|
else
|
||||||
|
prln_svrty_good "Ok"
|
||||||
|
fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok"
|
||||||
|
fi
|
||||||
|
|
||||||
outln
|
outln
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user