Since checking SSL key chains with openssl is sometimes a bit tricky, I have written a little perl script as wrapper around it.
Just download check_chain.zip, unzip it and run it as follows:
./check_chain.pl -p server port
-p (optional) prints out the certificates as well
server is the server to check
port (optional) is the port to connect to.
Here is the code:
#!/usr/bin/perl -w use strict; my $has_date_parse = 1; eval { require Date::Parse; Date::Parse->import( qw/str2time/ ) }; if ( $@ ne "" ) { print "Module Date::Parse not found! Skipping expiration date calculation!\n"; $has_date_parse = 0; } my $print_cert = 0; my $host = shift || &usage(); if ( $host eq '-p' ) { $print_cert = 1; $host = shift; } my $port = (shift || 443); my $output = `echo "\t" | openssl s_client -host $host -port $port -showcerts 2> /dev/null`; my $cert_num = 0; my $line; my @certs; my %certs; foreach $line ( split /\n/, $output ) { if ( $line =~ /BEGIN CERTIFICATE/ .. $line =~ /END CERTIFICATE/ ) { $certs[$cert_num] .= "$line\n"; } $cert_num += 1 if $line =~ /END CERTIFICATE/; } my $i; my $serial; my $attribute; foreach $i ( 0 .. scalar @certs -1 ) { $serial = qx{echo "$certs[$i]" | openssl x509 -serial -noout}; $serial =~ s/serial=//; chomp $serial; $certs{$serial}{'certificate'} = $certs[$i]; foreach $attribute ( qw/issuer subject issuer_hash subject_hash enddate/ ) { $certs{$serial}{"$attribute"} = qx{echo "$certs[$i]" | openssl x509 -$attribute -noout}; chomp $certs{$serial}{"$attribute"}; $certs{$serial}{"$attribute"} =~ s/notAfter=//; } # Certificate stored, reuse @certs to keep order with serial number $certs[$i] = $serial; $certs{$serial}{"order"} = $i; } print "\nChecked server: $host on port $port\n"; my $found; foreach $i ( 0 .. scalar @certs -1 ) { print "\nCertificate $i:\n"; $serial = $certs[$i]; print "\t", $certs{$serial}{"subject"}, " (hashed: ", $certs{$serial}{"subject_hash"}, ")\n"; print "\tserial number (check in browser): $serial\n"; print "\texpires: ", $certs{$serial}{"enddate"}; if ( $has_date_parse == 1 ) { print " (in ", int ((str2time($certs{$serial}{"enddate"}) - time) /60/60/24 ), " days)" ; } print "\n"; print "\t", $certs{$serial}{"issuer"}, " (hashed: ", $certs{$serial}{"issuer_hash"}, ")\n"; print "\tissuer found in chain: "; $found = "NO - should be root certificate"; if ( $certs{$serial}{"issuer_hash"} eq $certs{$serial}{"subject_hash"} ) { $found = "SELF SIGNED CERTIFICATE"; } else { foreach ( keys %certs ) { if ( $certs{$serial}{"issuer_hash"} eq $certs{$_}{"subject_hash"} ) { $found = "YES"; } } } print "$found\n"; } if ( $print_cert == 1 ) { foreach $i ( 0 .. scalar @certs -1 ) { print "\nCertificate $i:\n"; $serial = $certs[$i]; print $certs{$serial}{"certificate"} } } sub usage { print "Usage: $0 [-p] server_name [port]\n"; print "\t-p : also print certificates\n"; print "\tserver_name : server to check, mandatory\n"; print "\tport : port to check, default: 443\n"; exit; }
Its output is as follows:
$ ./check_chain.pl www.lavalite.de Checked server: www.lavalite.de on port 443 Certificate 0: subject= /C=DE/OU=Domain Control Validated/CN=www.lavalite.de (hashed: dad54aee) serial number (check in browser): 11213FB18C3CEF8B39A731AB874D155363F2 expires: Dec 8 11:13:28 2015 GMT (in 318 days) issuer= /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2 (hashed: 79701ca5) issuer found in chain: YES Certificate 1: subject= /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2 (hashed: 79701ca5) serial number (check in browser): 040000000001444EF03E20 expires: Feb 20 10:00:00 2024 GMT (in 3314 days) issuer= /C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA (hashed: b0f3e76e) issuer found in chain: NO - should be root certificate