# HG changeset patch # User Sergey Kandaurov # Date 1527715762 -10800 # Node ID b82ed2061f650a022d1977935530c53c29c7327e # Parent 0f2dd1f239baee00a899723f3c2b203d531ffd2d Tests: OCSP stapling tests. diff --git a/ssl_stapling.t b/ssl_stapling.t new file mode 100644 --- /dev/null +++ b/ssl_stapling.t @@ -0,0 +1,357 @@ +#!/usr/bin/perl + +# (C) Sergey Kandaurov +# (C) Nginx, Inc. + +# Tests for OCSP stapling. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +use MIME::Base64 qw/ decode_base64 /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +eval { require IO::Socket::SSL; }; +plan(skip_all => 'IO::Socket::SSL not installed') if $@; +eval { IO::Socket::SSL->can_ocsp() or die; }; +plan(skip_all => 'IO::Socket::SSL with OCSP support required') if $@; + +my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl') + ->plan(9)->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + ssl_stapling on; + ssl_trusted_certificate trusted.crt; + + ssl_certificate ec-end-int.crt; + ssl_certificate_key ec-end.key; + + ssl_certificate end-int.crt; + ssl_certificate_key end.key; + + server { + listen 127.0.0.1:8443 ssl; + listen 127.0.0.1:8080; + server_name localhost; + } + + server { + listen 127.0.0.1:8444 ssl; + server_name localhost; + + ssl_stapling_responder http://127.0.0.1:8081/; + } + + server { + listen 127.0.0.1:8445 ssl; + server_name localhost; + + ssl_stapling_verify on; + } + + server { + listen 127.0.0.1:8446 ssl; + server_name localhost; + + ssl_certificate ec-end.crt; + ssl_certificate_key ec-end.key; + } + + server { + listen 127.0.0.1:8447 ssl; + server_name localhost; + + ssl_certificate end-int.crt; + ssl_certificate_key end.key; + + ssl_stapling_file %%TESTDIR%%/resp.der; + } + + server { + listen 127.0.0.1:8448 ssl; + server_name localhost; + + ssl_certificate ec-end-int.crt; + ssl_certificate_key ec-end.key; + + ssl_stapling_file %%TESTDIR%%/ec-resp.der; + } + + server { + listen 127.0.0.1:8449 ssl; + server_name localhost; + + ssl_stapling_responder http://127.0.0.1:8080/; + } +} + +EOF + +my $d = $t->testdir(); +my $p = port(8081); + +$t->write_file('openssl.conf', <write_file('ca.conf', <>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +foreach my $name ('int', 'end') { + system("openssl req -new " + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.csr -keyout $d/$name.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +foreach my $name ('ec-end') { + system("openssl ecparam -genkey -out $d/$name.key -name prime256v1 " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create EC param: $!\n"; + system("openssl req -new -key $d/$name.key " + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.csr " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->write_file('certserial', '1000'); +$t->write_file('certindex', ''); + +system("openssl ca -batch -config $d/ca.conf " + . "-keyfile $d/root.key -cert $d/root.crt " + . "-subj /CN=int/ -in $d/int.csr -out $d/int.crt " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't sign certificate for int: $!\n"; + +system("openssl ca -batch -config $d/ca.conf " + . "-keyfile $d/int.key -cert $d/int.crt " + . "-subj /CN=ec-end/ -in $d/ec-end.csr -out $d/ec-end.crt " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't sign certificate for ec-end: $!\n"; + +system("openssl ca -batch -config $d/ca.conf " + . "-keyfile $d/int.key -cert $d/int.crt " + . "-subj /CN=end/ -in $d/end.csr -out $d/end.crt " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't sign certificate for end: $!\n"; + +# RFC 6960, serialNumber + +system("openssl x509 -in $d/end.crt -serial -noout " + . ">>$d/serial 2>>$d/openssl.out") == 0 + or die "Can't obtain serial for end: $!\n"; + +my $serial = pack("n2", 0x0202, hex $1) if $t->read_file('serial') =~ /(\d+)/; + +system("openssl ca -config $d/ca.conf -revoke $d/end.crt " + . "-keyfile $d/root.key -cert $d/root.crt " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't revoke end.crt: $!\n"; + +system("openssl ocsp -issuer $d/int.crt -cert $d/end.crt " + . "-reqout $d/req.der >>$d/openssl.out 2>&1") == 0 + or die "Can't create OCSP request: $!\n"; + +system("openssl ocsp -index $d/certindex -CA $d/int.crt " + . "-rsigner $d/root.crt -rkey $d/root.key " + . "-reqin $d/req.der -respout $d/resp.der -ndays 1 " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create OCSP response: $!\n"; + +system("openssl ocsp -issuer $d/int.crt -cert $d/ec-end.crt " + . "-reqout $d/ec-req.der >>$d/openssl.out 2>&1") == 0 + or die "Can't create EC OCSP request: $!\n"; + +system("openssl ocsp -index $d/certindex -CA $d/int.crt " + . "-rsigner $d/root.crt -rkey $d/root.key " + . "-reqin $d/ec-req.der -respout $d/ec-resp.der -ndays 1 " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create EC OCSP response: $!\n"; + +$t->write_file('trusted.crt', + $t->read_file('int.crt') . $t->read_file('root.crt')); +$t->write_file('end-int.crt', + $t->read_file('end.crt') . $t->read_file('int.crt')); +$t->write_file('ec-end-int.crt', + $t->read_file('ec-end.crt') . $t->read_file('int.crt')); + +$t->run_daemon(\&http_daemon, $t); +$t->run(); + +$t->waitforsocket("127.0.0.1:" . port(8081)); + +############################################################################### + +staple(8443, 'RSA'); +staple(8443, 'ECDSA'); +staple(8444, 'RSA'); +staple(8444, 'ECDSA'); +staple(8445, 'ECDSA'); +staple(8446, 'ECDSA'); +staple(8449, 'ECDSA'); + +sleep 1; + +ok(!staple(8443, 'RSA'), 'staple revoked'); +ok(staple(8443, 'ECDSA'), 'staple success'); + +ok(!staple(8444, 'RSA'), 'responder revoked'); +ok(staple(8444, 'ECDSA'), 'responder success'); + +ok(!staple(8445, 'ECDSA'), 'verify - root not trusted'); + +ok(staple(8446, 'ECDSA', "$d/int.crt"), 'cert store'); + +is(staple(8447, 'RSA'), '1 1', 'file revoked'); +is(staple(8448, 'ECDSA'), '1 0', 'file success'); + +ok(!staple(8449, 'ECDSA'), 'ocsp error'); + +############################################################################### + +sub staple { + my ($port, $ciphers, $ca) = @_; + my (@resp); + + my $staple_cb = sub { + my ($ssl, $resp) = @_; + push @resp, !!$resp; + return 1 unless $resp; + my $obj = $ssl->_get_ssl_object; + my $cert = Net::SSLeay::get_peer_certificate($obj); + my $certid = eval { Net::SSLeay::OCSP_cert2ids($obj, $cert) } + or do { die "no OCSP_CERTID for certificate: $@"; }; + + my @res = Net::SSLeay::OCSP_response_results($resp, $certid); + push @resp, $res[0][2]->{'statusType'}; + }; + + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + local $SIG{PIPE} = sub { die "sigpipe\n" }; + alarm(2); + IO::Socket::SSL->new( + Proto => 'tcp', + PeerAddr => '127.0.0.1', + PeerPort => port($port), + SSL_cipher_list => $ciphers, + SSL_ca_file => $ca, + SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), + SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_TRY_STAPLE(), + SSL_ocsp_staple_callback => $staple_cb, + SSL_error_trap => sub { die $_[1] } + ); + alarm(0); + }; + alarm(0); + + if ($@) { + log_in("died: $@"); + return undef; + } + + return join ' ', @resp; +} + +############################################################################### + +sub http_daemon { + my ($t) = shift; + my $server = IO::Socket::INET->new( + Proto => 'tcp', + LocalHost => "127.0.0.1:" . port(8081), + Listen => 5, + Reuse => 1 + ) + or die "Can't create listening socket: $!\n"; + + local $SIG{PIPE} = 'IGNORE'; + + while (my $client = $server->accept()) { + $client->autoflush(1); + + my $headers = ''; + my $uri = ''; + + while (<$client>) { + $headers .= $_; + last if (/^\x0d?\x0a?$/); + } + + $uri = $1 if $headers =~ /^\S+\s+\/([^ ]+)\s+HTTP/i; + next unless $uri; + + $uri =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; + my $req = decode_base64($uri); + my $resp = index($req, $serial) > 0 ? 'resp' : 'ec-resp'; + + # ocsp dummy handler + + select undef, undef, undef, 0.02; + + $headers = <<"EOF"; +HTTP/1.1 200 OK +Connection: close +Content-Type: application/ocsp-response + +EOF + + print $client $headers . $t->read_file("$resp.der"); + } +} + +###############################################################################