A recent project required me to write a automated SSL certificate checker that would ping me every-time a SSL renewal is due. The project required to parse a handful of client sites and get the SSL information.
Reading a SSL certificate directly from a domain is relatively simple in PHP; a working example function is given below.
/**
* Takes a domain name and returns SSL certificate information
*
* @param string $domain_name
* @return array $certinfo
*/
function getSSL($domain_name)
{
$errno = 0;
$errstr = '';
// Set socket connection timeout
$timeout = 30;
// Create a stream context to open a ssl socket
// Set options to get ssl certificate
// https://www.php.net/manual/en/context.php
// https://www.php.net/manual/en/context.ssl.php
$ssl_info = stream_context_create(array(
"ssl" => array(
"capture_peer_cert" => TRUE)));
$stream = stream_socket_client("ssl://" . $domain_name . ":443",
$errno,
$errstr,
$timeout,
STREAM_CLIENT_CONNECT,
$ssl_info);
if (!$stream) {
echo "ERROR: $errno - $errstr";
} else {
$cert_resource = stream_context_get_params($stream);
$certificate = $cert_resource['options']['ssl']['peer_certificate'];
$certinfo = openssl_x509_parse($certificate);
fclose($stream);
return $certinfo;
}
}
Reading SSL certificate from a web domain
Sample request to read a SSL certificate information from a website is given below.
$certinfo = getSSL("codediesel.com");
print_r($certinfo);
// print_r($certinfo) output
[O] => ZeroSSL
[CN] => ZeroSSL RSA Domain Secure Site CA
)
[version] => 2
[serialNumber] => 0x8A1AB23A8C5EA0EEA9F22A26B58F9626
[serialNumberHex] => 8A1AB23A8C5EA0EEA9F22A26B58F9626
[validFrom] => 201207000000Z
[validTo] => 210307235959Z
[validFrom_time_t] => 1607299200
[validTo_time_t] => 1615161599
[signatureTypeSN] => RSA-SHA384
[signatureTypeLN] => sha384WithRSAEncryption
[signatureTypeNID] => 669
[purposes] => Array
(
[1] => Array
(
[0] => 1
[1] =>
[2] => sslclient
)
.
.
.
Signature : ecdsa-with-SHA256
30:45:02:21:00:86:8F:B0:60:31:E9:27:06:EE:37:FB:
F3:05:8C:BE:CA:6C:81:81:0B:E0:48:4B:DF:CB:39:4E:
6B:04:48:A1:46:02:20:08:3D:0E:8A:A9:E2:0E:6B:6B:
BB:C5:BE:72:3F:DF:08:E9:0D:B5:E4:1C:AE:B3:BC:D1:
DC:AF:B7:F1:34:59:D1
[subjectAltName] => DNS:codediesel.com, DNS:www.codediesel.com
)
)
We can read the validity of the certificate using the fields retrieved from the certificate.
$valid_from = date(DATE_RFC2822, $certinfo['validFrom_time_t']);
$valid_to = date(DATE_RFC2822, $certinfo['validTo_time_t']);
Or count the number of days to expire.
if( $certinfo['validFrom_time_t'] > time() || $certinfo['validTo_time_t'] < time() ) {
print "Certificate is expired.";
} else {
$start = date_create(date(DATE_RFC2822, $certinfo['validTo_time_t']));
$end = date_create(); // Current time and date
$diff = date_diff( $start, $end );
print "Certificate will expire in " . $diff->days . " days";
}
Note: You need to set the default timezone in your code to get accurate results regarding time calculations. More discussion can be found at the following link:
https://stackoverflow.com/questions/2040560/finding-the-number-of-days-between-two-dates
Reading SSL certificate from a file
If you have a certificate file with you (usually with a .crt extension), you can use the following code to get the required information from the file.
$cert = file_get_contents('certificate.crt');
$certinfo = openssl_x509_parse($cert);
print_r($certinfo);
The string passed to the ‘openssl_x509_parse’ should be the content of a certificate, PEM encoded, which need to start with —–BEGIN CERTIFICATE—–.
very well explained and was very help full. Thanks
very well explained and very helpfull to us .thanks