オンラインで証明書の期限切れをチェック
モジュールを使うのじゃ
昨日の作ったチェックツールはNet::SSL::ExpireDateを使っていなかったので,今日は使うようにしてみました.id:hirose31さんのNet::SSL::ExpireDate + Test::Base で証明書の期限切れをチェック - (ひ)メモを見てやっとTest::Baseの使い方がわかってきたので,それも利用.成功しているときには
ok 1 - foo.co.jp ok 2 - foo.co.jp expired 2007-03-29T23:59:59 ok 3 # skip bar.foo.co.jp not using https ok 4 # skip bar.foo.co.jp not using https
失敗したときには
not ok 5 - hoge.foo.co.jp expired 2006-07-15T23:59:59 # Failed test 'hoge.foo.co.jp expired 2006-07-15T23:59:59' # in ./fraud3.pl at line 79. # got: '2006-07-15T23:59:59' # expected: undef not ok 6 - hoge.foo.co.jp # Failed test 'hoge.foo.co.jp' # in ./fraud3.pl at line 77. # 'hoge2.foo.co.jp' # doesn't match '(?i-xsm:hoge.foo.co.jp)'
のように出力します.
スクリプト
#!/usr/local/bin/perl { package Net::SSL::ExpireDate::WithCnCheck; use base qw ( Net::SSL::ExpireDate ); use Date::Parse; use Carp; sub expire2_date { my $self = shift; if ( !$self->{expire_date} ) { if ( $self->{type} eq 'https' ) { my ( $host, $port ) = split /:/, $self->{target}, 2; $port ||= 443; ### $host ### $port my $sock = IO::Socket::SSL->new("$host:$port"); croak IO::Socket::SSL::errstr() if !$sock; my $cert = $sock->peer_certificate(); my $expire_date_asn1 = Net::SSLeay::X509_get_notAfter($cert); my $expire_date_str = Net::SSLeay::P_ASN1_UTCTIME_put2string($expire_date_asn1); ### $expire_date_str my $begin_date_asn1 = Net::SSLeay::X509_get_notBefore($cert); my $begin_date_str = Net::SSLeay::P_ASN1_UTCTIME_put2string($begin_date_asn1); ### $begin_date_str my $sub = Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name($cert) ); ( $self->{cn} ) = $sub =~ m{CN=(\S+)}; $sock->close; $self->{expire_date} = DateTime->from_epoch( epoch => str2time($expire_date_str) ); $self->{begin_date} = DateTime->from_epoch( epoch => str2time($begin_date_str) ); } else { croak "you need https as type for expire2_date"; } } return $self->{expire_date}; } sub has_cn { my $self = shift; return $self->{cn}; } } package main; use Test::Base; my $duration = '1 week'; plan tests => 2 * blocks; run { my $block = shift; my ( $host, $ed ); $host = $block->name(); $ed = Net::SSL::ExpireDate::WithCnCheck->new( https => $host ); eval { $ed->expire2_date(); }; SKIP: { skip "$host not using https", 2 if ($@); like( $ed->has_cn(), qr{$host}i, $host ); my $ex = $ed->expire_date(); is( $ed->is_expired($duration) && $ed->expire_date->iso8601, undef, "$host expired $ex"); } } __DATA__ === foo.co.jp === bar.foo.co.jp ...
ちょっとした解説
expire_dateの中で証明書のCommonNameを抽出したかったので,Net::SSL::ExpireDate::WithCnCheckというモジュールを作って,その中でexpire2_dateというメソッドとhas_cnというメソッドを追加してみました.expire2_dateでの変更点はCommonNameを抽出しているところだけで,
my $sub = Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name($cert) ); ( $self->{cn} ) = $sub =~ m{CN=(\S+)};
というもの.has_cnは↑で$self->{cn}に蓄えたホスト名を取得しています.get_cnというメソッド名の方がよかったかも.その他,昨日と違う点は,
SKIP: { skip "$host not using https", 2 if ($@);
という形で,httpsに対応していないサーバの場合はskipという表示をするようにしたこと,
like( $ed->has_cn(), qr{$host}i, $host );
CommonNameに大文字でホスト名が書いてある場合用に正規表現を使ったことです.