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