最近的一个项目要求我写一个自动的SSL证书检查器,在每次SSL续费到期时都会向我发出提示。这个项目需要解析少量的客户网站并获得SSL信息。
在PHP中直接读取一个域名的SSL证书是比较简单的;下面给出了一个工作的例子函数。
/**
* 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;
}
}
从一个域名读取SSL证书
下面给出了从一个网站读取SSL证书信息的请求示例。
$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
)
)
我们可以使用从证书中检索的字段来读取证书的有效性。
$valid_from = date(DATE_RFC2822, $certinfo['validFrom_time_t']);
$valid_to = date(DATE_RFC2822, $certinfo['validTo_time_t']);
或者计算过期的天数。
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";
}
注意:你需要在代码中设置默认的时区,以获得关于时间计算的准确结果。更多的讨论可以在以下链接中找到:
stackoverflow.com/questions/2…
从文件中读取SSL证书
如果你有一个证书文件(通常有.crt扩展名),你可以使用下面的代码从文件中获得所需的信息。
$cert = file_get_contents('certificate.crt');
$certinfo = openssl_x509_parse($cert);
print_r($certinfo);
传递给 "openssl_x509_parse "的字符串应该是证书的内容,PEM编码,需要以--BEGIN CERTIFICATE--开头。