個人メモとして記事っぽくまとめ。
PHPでドメインのSSL証明書の有効期限を取得するコードは先人たちの英知に学ぶとして大幅に前段を省略しますが、最後はopenssl_x509_parseで返ってきた値の[subject][CN]と対象ドメインを比較して有効期限の取得判断をしています。
この時面倒なのが、例えばwww.yahoo.co.jpでこれを調べてみると、
のように*付きでくるので単純比較では不一致になってしまうことですね。
まぁこれは正規表現でも使ってあげればいいので、脳死コーディングするなら
if( strpos($parsed['subject']['CN'], $domain_name) !== false ||
preg_match(sprintf('/^.%s$/',$parsed['subject']['CN']), $domain_name) === 1 )
{
echo '有効期限:' . date('Y/m/d', $parsed['validTo_time_t']);
}
とでもしてあげればよいのですが、面倒なものだと、
とかあるわけですね。
そんなときは、[extensions]の[subjectAltName]まで見てあげないといけないはずです。
うわっめんどい、と一瞬思いますが、単純なパース処理なので、難しく考えずexplode、foreach、preg_matchあたりを使えばできるかと。余計な空白もあるのでtrimも忘れずに。
まぁここまで厳密に有効期限チェックするような監視よろしくなバリバリの仕組みでなければ、せいぜい*あたりだけ見てあげればいい気はしますね。知らんけど。
とここで終わるのもなんなので、
$arrDomain = [
'qiita.com',
'www.youtube.com',
'www.google.co.jp',
'www.yahoo.co.jp',
];
foreach($arrDomain as $domain_name) {
echo '■' . $domain_name . PHP_EOL;
$stream_context = stream_context_create(array(
'ssl' => array('capture_peer_cert' => true)
));
$resource = stream_socket_client(
'ssl://' . $domain_name . ':443',
$errno,
$errstr,
30,
STREAM_CLIENT_CONNECT,
$stream_context
);
$cont = stream_context_get_params($resource);
$parsed = openssl_x509_parse($cont['options']['ssl']['peer_certificate']);
$func_chk_domain = function($cn, $domain) {
return (strpos($cn, $domain) !== false) ||
(preg_match(sprintf('/^.%s$/',$cn), $domain) === 1);
};
// 単純にCNで比較(完全一致と*時の正規表現)
$check_flg = false;
if( $func_chk_domain($parsed['subject']['CN'], $domain_name) ) {
echo 'CNで一致' . PHP_EOL;
$check_flg = true;
}
else {
// CNで一致しない場合はextensionsのsubjectAltNameを精査
$arrDNS = explode(',', $parsed['extensions']['subjectAltName']);
foreach($arrDNS as $dns) {
if( $func_chk_domain(str_ireplace(['DNS:',' '],'',$dns), $domain_name) ) {
echo 'subjectAltNameで一致' . PHP_EOL;
$check_flg = true;
break;
}
}
}
if( $check_flg ) {
echo '有効期限:' . date('Y/m/d', $parsed['validTo_time_t']) . PHP_EOL;
}
else {
echo '取得NG' . PHP_EOL;
}
}
で取れました。