在PHP中读取SSL证书的方法

534 阅读1分钟

最近的一个项目要求我写一个自动的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--开头。