lennysan has asked for the wisdom of the Perl Monks concerning the following question:

I've had some code running for the past few years that reports if the a given domain's SSL certificate is invalid. Recently I've come across one domain where it is telling me that the cert is invalid, but in reality the certificate is perfectly fine (as far as I can tell). I was hoping a few eyes on this could find the problem I've been missing. Here is the code:
#!/usr/bin/perl checkCert('www.clevelandorchestra.com',''); ## Verify the SSL certificate of the given path sub checkCert { my ($host, $port) = @_; ## Get the host domain and port from the path $port = $port ? $port : '443'; $ENV{HTTPS_CA_FILE} = 'cacert.pem'; my $cert_error_message = ''; ## Create the connection to the server. require Net::SSL; my $sock; eval { $sock = Net::SSL->new(PeerAddr => $host, PeerPort => $port, Ti +meout => 5); }; ## If we are unable to connect, give an error. This also may ## mean the certificate authority is invalid. ## Check #1: Valid CA if($@) { $cert_error_message = "SSL certificate invalid or is not from +a trusted source: $@"; $cert_error_message =~ s/ at .+$//s; } else { ## Check #2: Domain matches CN ## Check that the domain name matched the certificate common n +ame my $cert = $sock->get_peer_certificate; my $subject = $cert->subject_name; $subject =~ s/\*//g; # Remove wildcard astricks my ($CN) = $subject =~ /CN=(.*)\W/; if(!$CN) { $cert_error_message = "Unable to read SSL certificate"; } elsif($host !~ /$CN/i) { $cert_error_message = "SSL Certificate Common Name mismatc +h. Retrieved common name '$CN' does not match hostname '$h ost'"; } ## Check #3: Check for expired my $startsOn = getSSLTime($cert->not_before); my $expiresOn = getSSLTime($cert->not_after); my $currTime = time; ## Check that the certificate start time is not after the curr +ent time if($currTime < $startsOn) { $cert_error_message = "SSL Certificate is not valid before + ". $cert->not_before; } if($currTime > $expiresOn) { $cert_error_message = "SSL Certificate has expired, not va +lid after ". $cert->not_after; } } ## If we found an error, set the global error message string and s +ignal there was an error if($cert_error_message) { print "$cert_error_message at $host:$por +t\n"; } else { print "SUCCESS: SSL Cerificate verified\n"; } delete $ENV{HTTPS_CA_FILE}; return 1; } ## Given a string in the format '$year-$month-$day $hour:$minute:$seco +nd', return ## the epoch time in GMT time. ## Used by checkCert. sub getSSLTime { my ($string) = @_; require Time::Local; my ($year, $month, $day, $hour, $minute,$second,$GMT) = $string =~ + /^(.+)-(.+)-(.+) (.+):(.+):(\S+)( GMT)?$/; $year -= 1900; $month--; return Time::Local::timegm($second,$minute,$hour,$day,$month,$year +); }
You can put in any other domain that works over https and it works great. This specific domain though is what baffles me. Note for the cacert.pem file, I've been using: http://curl.haxx.se/ca/cacert.pem Note that I would really like to keep this code as unchanged as possible. It's been battle tested fairly well over the past few years, and so I'm hoping there's a small fix I can add that would allow me to keep most of the logic. Thanks to everyone in advance!

Replies are listed 'Best First'.
Re: Checking the validity of an SSL cert
by Anonymous Monk on Dec 02, 2008 at 03:35 UTC
    Net::SSL does checking by trying to download https://.../ which won't work for www.clevelandorchestra.com, because it keeps redirecting to http://www.clevelandorchestra.com/. They do appear to have a valid certificate (check with SSL Certificate Check]). You might have better luck with OpenCA::OpenSSL or something like it.