PHP获取SSL证书信息,获取SSL证书序列号,字符串转16进制

lys2023年04月10日 0条评论
<?php


class SslCertService{

    public static function certInfo($domain){
        $keywordWebsite = 'http://' . str_replace(['https://', 'http://'], '', $domain);
        $keywordWebsiteArr = parse_url($keywordWebsite);
        $issuerDn = 'Sectigo';
        if (empty($keywordWebsiteArr['host'])) {
            $cert_info = [];
            $cert_info['CN'] = $keywordWebsiteArr['host'];
            $cert_info['startTime'] = '';
            $cert_info['endTime'] =  '';
            $cert_info['signature'] =  '';
            $cert_info['publicKey'] =  '';
            $cert_info['serialNumber16'] = '';
            $cert_info['issuerDn'] = $issuerDn;
            return $cert_info;
        }
        $host = $keywordWebsiteArr['host'];
        $cert_info = self::getCertInfo($host);
        if (false == $cert_info) {
            $cert_info = [];
            $cert_info['CN'] = $keywordWebsiteArr['host'];
            $cert_info['startTime'] = '';
            $cert_info['endTime'] =  '';
            $cert_info['signature'] =  '';
            $cert_info['publicKey'] =  '';
            $cert_info['serialNumber16'] = '';
            $cert_info['issuerDn'] = $issuerDn;
            return $cert_info;
        }
        $dn = $cert_info['subject']['CN']; //证书保护域名
        $issuerDn = $cert_info['issuer']['CN']; //证书颁发者
        $validFrom_time_t = date('Y-m-d H:i:s', $cert_info['validFrom_time_t']); //证书开始时间
        $validTo_time_d = date('Y-m-d H:i:s', $cert_info['validTo_time_t']); //证书结束时间
        $signatureTypeSN = $cert_info['signatureTypeSN']; //签名算法
        $signatureTypeLN = $cert_info['signatureTypeLN']; //公钥
        $cert_info['CN'] = Tool::decodeKeyword($dn);
        $cert_info['startTime'] = $validFrom_time_t;
        $cert_info['endTime'] = $validTo_time_d;
        $cert_info['signature'] = $signatureTypeSN;
        $cert_info['publicKey'] = strstr($signatureTypeSN, 'RSA') ? 'RSA (2048bits)' : $signatureTypeLN;
        $cert_info['issuerDn'] = $issuerDn;
        $cert_info['serialNumber16'] = self::strTo16($cert_info['serialNumber']);
        $serialNumber16Arr = str_split($cert_info['serialNumber16']);
        $cert_info['serialNumber16'] = '';
        foreach ($serialNumber16Arr as $index => $value){
            if($index !=0 && $index % 2 == 0){
                $cert_info['serialNumber16'] .= ':'.$value;
            }else{
                $cert_info['serialNumber16'] .= $value;
            }
        }
        return $cert_info;
    }


    public static function getCertInfo($domain){
        $domain = str_replace(['https://','http://'], '', $domain);
        $domain =  Punycode::encode($domain);
        $context = stream_context_create(['ssl' => [
            'capture_peer_cert' => true,
            'capture_peer_cert_chain' => false,
        ],
        ]);
        $client = @stream_socket_client("ssl://".$domain.":443", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
        if($client==false) {
            return false;
        }
        $params = stream_context_get_params($client);
        $cert = $params['options']['ssl']['peer_certificate'];
        $cert_info = openssl_x509_parse($cert);
        return $cert_info;
    }


    /**
     * 字符串转16进制
     * @param $serialNumber
     * @return bool|string
     */
    public static function strTo16($serialNumber){
        $serial = $serialNumber . "";
        $base = bcpow("2", "32");
        $counter = 100;
        $res = "";
        $val = $serial;
        while($counter > 0 && $val > 0) {
            $counter = $counter - 1;
            $tmpres = dechex(bcmod($val, $base)) . "";
            /* adjust for 0's */
            for ($i = 8-strlen($tmpres); $i > 0; $i = $i-1) {
                $tmpres = "0$tmpres";
            }
            $res = $tmpres .$res;
            $val = bcdiv($val, $base);
        }
        if ($counter <= 0) {
            return false;
        }
        return strtoupper($res);
    }
}