Mercurial > hg > nginx-tests
comparison ssl_stapling.t @ 1330:b82ed2061f65
Tests: OCSP stapling tests.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Thu, 31 May 2018 00:29:22 +0300 |
parents | |
children | 73a9504ae6fd |
comparison
equal
deleted
inserted
replaced
1329:0f2dd1f239ba | 1330:b82ed2061f65 |
---|---|
1 #!/usr/bin/perl | |
2 | |
3 # (C) Sergey Kandaurov | |
4 # (C) Nginx, Inc. | |
5 | |
6 # Tests for OCSP stapling. | |
7 | |
8 ############################################################################### | |
9 | |
10 use warnings; | |
11 use strict; | |
12 | |
13 use Test::More; | |
14 | |
15 use MIME::Base64 qw/ decode_base64 /; | |
16 | |
17 BEGIN { use FindBin; chdir($FindBin::Bin); } | |
18 | |
19 use lib 'lib'; | |
20 use Test::Nginx; | |
21 | |
22 ############################################################################### | |
23 | |
24 select STDERR; $| = 1; | |
25 select STDOUT; $| = 1; | |
26 | |
27 eval { require IO::Socket::SSL; }; | |
28 plan(skip_all => 'IO::Socket::SSL not installed') if $@; | |
29 eval { IO::Socket::SSL->can_ocsp() or die; }; | |
30 plan(skip_all => 'IO::Socket::SSL with OCSP support required') if $@; | |
31 | |
32 my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl') | |
33 ->plan(9)->write_file_expand('nginx.conf', <<'EOF'); | |
34 | |
35 %%TEST_GLOBALS%% | |
36 | |
37 daemon off; | |
38 | |
39 events { | |
40 } | |
41 | |
42 http { | |
43 %%TEST_GLOBALS_HTTP%% | |
44 | |
45 ssl_stapling on; | |
46 ssl_trusted_certificate trusted.crt; | |
47 | |
48 ssl_certificate ec-end-int.crt; | |
49 ssl_certificate_key ec-end.key; | |
50 | |
51 ssl_certificate end-int.crt; | |
52 ssl_certificate_key end.key; | |
53 | |
54 server { | |
55 listen 127.0.0.1:8443 ssl; | |
56 listen 127.0.0.1:8080; | |
57 server_name localhost; | |
58 } | |
59 | |
60 server { | |
61 listen 127.0.0.1:8444 ssl; | |
62 server_name localhost; | |
63 | |
64 ssl_stapling_responder http://127.0.0.1:8081/; | |
65 } | |
66 | |
67 server { | |
68 listen 127.0.0.1:8445 ssl; | |
69 server_name localhost; | |
70 | |
71 ssl_stapling_verify on; | |
72 } | |
73 | |
74 server { | |
75 listen 127.0.0.1:8446 ssl; | |
76 server_name localhost; | |
77 | |
78 ssl_certificate ec-end.crt; | |
79 ssl_certificate_key ec-end.key; | |
80 } | |
81 | |
82 server { | |
83 listen 127.0.0.1:8447 ssl; | |
84 server_name localhost; | |
85 | |
86 ssl_certificate end-int.crt; | |
87 ssl_certificate_key end.key; | |
88 | |
89 ssl_stapling_file %%TESTDIR%%/resp.der; | |
90 } | |
91 | |
92 server { | |
93 listen 127.0.0.1:8448 ssl; | |
94 server_name localhost; | |
95 | |
96 ssl_certificate ec-end-int.crt; | |
97 ssl_certificate_key ec-end.key; | |
98 | |
99 ssl_stapling_file %%TESTDIR%%/ec-resp.der; | |
100 } | |
101 | |
102 server { | |
103 listen 127.0.0.1:8449 ssl; | |
104 server_name localhost; | |
105 | |
106 ssl_stapling_responder http://127.0.0.1:8080/; | |
107 } | |
108 } | |
109 | |
110 EOF | |
111 | |
112 my $d = $t->testdir(); | |
113 my $p = port(8081); | |
114 | |
115 $t->write_file('openssl.conf', <<EOF); | |
116 [ req ] | |
117 default_bits = 1024 | |
118 encrypt_key = no | |
119 distinguished_name = req_distinguished_name | |
120 [ req_distinguished_name ] | |
121 EOF | |
122 | |
123 $t->write_file('ca.conf', <<EOF); | |
124 [ ca ] | |
125 default_ca = myca | |
126 | |
127 [ myca ] | |
128 new_certs_dir = $d | |
129 database = $d/certindex | |
130 default_md = sha1 | |
131 policy = myca_policy | |
132 serial = $d/certserial | |
133 default_days = 1 | |
134 x509_extensions = myca_extensions | |
135 | |
136 [ myca_policy ] | |
137 commonName = supplied | |
138 | |
139 [ myca_extensions ] | |
140 basicConstraints = critical,CA:TRUE | |
141 authorityInfoAccess = OCSP;URI:http://127.0.0.1:$p | |
142 EOF | |
143 | |
144 foreach my $name ('root') { | |
145 system('openssl req -x509 -new ' | |
146 . "-config $d/openssl.conf -subj /CN=$name/ " | |
147 . "-out $d/$name.crt -keyout $d/$name.key " | |
148 . ">>$d/openssl.out 2>&1") == 0 | |
149 or die "Can't create certificate for $name: $!\n"; | |
150 } | |
151 | |
152 foreach my $name ('int', 'end') { | |
153 system("openssl req -new " | |
154 . "-config $d/openssl.conf -subj /CN=$name/ " | |
155 . "-out $d/$name.csr -keyout $d/$name.key " | |
156 . ">>$d/openssl.out 2>&1") == 0 | |
157 or die "Can't create certificate for $name: $!\n"; | |
158 } | |
159 | |
160 foreach my $name ('ec-end') { | |
161 system("openssl ecparam -genkey -out $d/$name.key -name prime256v1 " | |
162 . ">>$d/openssl.out 2>&1") == 0 | |
163 or die "Can't create EC param: $!\n"; | |
164 system("openssl req -new -key $d/$name.key " | |
165 . "-config $d/openssl.conf -subj /CN=$name/ " | |
166 . "-out $d/$name.csr " | |
167 . ">>$d/openssl.out 2>&1") == 0 | |
168 or die "Can't create certificate for $name: $!\n"; | |
169 } | |
170 | |
171 $t->write_file('certserial', '1000'); | |
172 $t->write_file('certindex', ''); | |
173 | |
174 system("openssl ca -batch -config $d/ca.conf " | |
175 . "-keyfile $d/root.key -cert $d/root.crt " | |
176 . "-subj /CN=int/ -in $d/int.csr -out $d/int.crt " | |
177 . ">>$d/openssl.out 2>&1") == 0 | |
178 or die "Can't sign certificate for int: $!\n"; | |
179 | |
180 system("openssl ca -batch -config $d/ca.conf " | |
181 . "-keyfile $d/int.key -cert $d/int.crt " | |
182 . "-subj /CN=ec-end/ -in $d/ec-end.csr -out $d/ec-end.crt " | |
183 . ">>$d/openssl.out 2>&1") == 0 | |
184 or die "Can't sign certificate for ec-end: $!\n"; | |
185 | |
186 system("openssl ca -batch -config $d/ca.conf " | |
187 . "-keyfile $d/int.key -cert $d/int.crt " | |
188 . "-subj /CN=end/ -in $d/end.csr -out $d/end.crt " | |
189 . ">>$d/openssl.out 2>&1") == 0 | |
190 or die "Can't sign certificate for end: $!\n"; | |
191 | |
192 # RFC 6960, serialNumber | |
193 | |
194 system("openssl x509 -in $d/end.crt -serial -noout " | |
195 . ">>$d/serial 2>>$d/openssl.out") == 0 | |
196 or die "Can't obtain serial for end: $!\n"; | |
197 | |
198 my $serial = pack("n2", 0x0202, hex $1) if $t->read_file('serial') =~ /(\d+)/; | |
199 | |
200 system("openssl ca -config $d/ca.conf -revoke $d/end.crt " | |
201 . "-keyfile $d/root.key -cert $d/root.crt " | |
202 . ">>$d/openssl.out 2>&1") == 0 | |
203 or die "Can't revoke end.crt: $!\n"; | |
204 | |
205 system("openssl ocsp -issuer $d/int.crt -cert $d/end.crt " | |
206 . "-reqout $d/req.der >>$d/openssl.out 2>&1") == 0 | |
207 or die "Can't create OCSP request: $!\n"; | |
208 | |
209 system("openssl ocsp -index $d/certindex -CA $d/int.crt " | |
210 . "-rsigner $d/root.crt -rkey $d/root.key " | |
211 . "-reqin $d/req.der -respout $d/resp.der -ndays 1 " | |
212 . ">>$d/openssl.out 2>&1") == 0 | |
213 or die "Can't create OCSP response: $!\n"; | |
214 | |
215 system("openssl ocsp -issuer $d/int.crt -cert $d/ec-end.crt " | |
216 . "-reqout $d/ec-req.der >>$d/openssl.out 2>&1") == 0 | |
217 or die "Can't create EC OCSP request: $!\n"; | |
218 | |
219 system("openssl ocsp -index $d/certindex -CA $d/int.crt " | |
220 . "-rsigner $d/root.crt -rkey $d/root.key " | |
221 . "-reqin $d/ec-req.der -respout $d/ec-resp.der -ndays 1 " | |
222 . ">>$d/openssl.out 2>&1") == 0 | |
223 or die "Can't create EC OCSP response: $!\n"; | |
224 | |
225 $t->write_file('trusted.crt', | |
226 $t->read_file('int.crt') . $t->read_file('root.crt')); | |
227 $t->write_file('end-int.crt', | |
228 $t->read_file('end.crt') . $t->read_file('int.crt')); | |
229 $t->write_file('ec-end-int.crt', | |
230 $t->read_file('ec-end.crt') . $t->read_file('int.crt')); | |
231 | |
232 $t->run_daemon(\&http_daemon, $t); | |
233 $t->run(); | |
234 | |
235 $t->waitforsocket("127.0.0.1:" . port(8081)); | |
236 | |
237 ############################################################################### | |
238 | |
239 staple(8443, 'RSA'); | |
240 staple(8443, 'ECDSA'); | |
241 staple(8444, 'RSA'); | |
242 staple(8444, 'ECDSA'); | |
243 staple(8445, 'ECDSA'); | |
244 staple(8446, 'ECDSA'); | |
245 staple(8449, 'ECDSA'); | |
246 | |
247 sleep 1; | |
248 | |
249 ok(!staple(8443, 'RSA'), 'staple revoked'); | |
250 ok(staple(8443, 'ECDSA'), 'staple success'); | |
251 | |
252 ok(!staple(8444, 'RSA'), 'responder revoked'); | |
253 ok(staple(8444, 'ECDSA'), 'responder success'); | |
254 | |
255 ok(!staple(8445, 'ECDSA'), 'verify - root not trusted'); | |
256 | |
257 ok(staple(8446, 'ECDSA', "$d/int.crt"), 'cert store'); | |
258 | |
259 is(staple(8447, 'RSA'), '1 1', 'file revoked'); | |
260 is(staple(8448, 'ECDSA'), '1 0', 'file success'); | |
261 | |
262 ok(!staple(8449, 'ECDSA'), 'ocsp error'); | |
263 | |
264 ############################################################################### | |
265 | |
266 sub staple { | |
267 my ($port, $ciphers, $ca) = @_; | |
268 my (@resp); | |
269 | |
270 my $staple_cb = sub { | |
271 my ($ssl, $resp) = @_; | |
272 push @resp, !!$resp; | |
273 return 1 unless $resp; | |
274 my $obj = $ssl->_get_ssl_object; | |
275 my $cert = Net::SSLeay::get_peer_certificate($obj); | |
276 my $certid = eval { Net::SSLeay::OCSP_cert2ids($obj, $cert) } | |
277 or do { die "no OCSP_CERTID for certificate: $@"; }; | |
278 | |
279 my @res = Net::SSLeay::OCSP_response_results($resp, $certid); | |
280 push @resp, $res[0][2]->{'statusType'}; | |
281 }; | |
282 | |
283 eval { | |
284 local $SIG{ALRM} = sub { die "timeout\n" }; | |
285 local $SIG{PIPE} = sub { die "sigpipe\n" }; | |
286 alarm(2); | |
287 IO::Socket::SSL->new( | |
288 Proto => 'tcp', | |
289 PeerAddr => '127.0.0.1', | |
290 PeerPort => port($port), | |
291 SSL_cipher_list => $ciphers, | |
292 SSL_ca_file => $ca, | |
293 SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), | |
294 SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_TRY_STAPLE(), | |
295 SSL_ocsp_staple_callback => $staple_cb, | |
296 SSL_error_trap => sub { die $_[1] } | |
297 ); | |
298 alarm(0); | |
299 }; | |
300 alarm(0); | |
301 | |
302 if ($@) { | |
303 log_in("died: $@"); | |
304 return undef; | |
305 } | |
306 | |
307 return join ' ', @resp; | |
308 } | |
309 | |
310 ############################################################################### | |
311 | |
312 sub http_daemon { | |
313 my ($t) = shift; | |
314 my $server = IO::Socket::INET->new( | |
315 Proto => 'tcp', | |
316 LocalHost => "127.0.0.1:" . port(8081), | |
317 Listen => 5, | |
318 Reuse => 1 | |
319 ) | |
320 or die "Can't create listening socket: $!\n"; | |
321 | |
322 local $SIG{PIPE} = 'IGNORE'; | |
323 | |
324 while (my $client = $server->accept()) { | |
325 $client->autoflush(1); | |
326 | |
327 my $headers = ''; | |
328 my $uri = ''; | |
329 | |
330 while (<$client>) { | |
331 $headers .= $_; | |
332 last if (/^\x0d?\x0a?$/); | |
333 } | |
334 | |
335 $uri = $1 if $headers =~ /^\S+\s+\/([^ ]+)\s+HTTP/i; | |
336 next unless $uri; | |
337 | |
338 $uri =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; | |
339 my $req = decode_base64($uri); | |
340 my $resp = index($req, $serial) > 0 ? 'resp' : 'ec-resp'; | |
341 | |
342 # ocsp dummy handler | |
343 | |
344 select undef, undef, undef, 0.02; | |
345 | |
346 $headers = <<"EOF"; | |
347 HTTP/1.1 200 OK | |
348 Connection: close | |
349 Content-Type: application/ocsp-response | |
350 | |
351 EOF | |
352 | |
353 print $client $headers . $t->read_file("$resp.der"); | |
354 } | |
355 } | |
356 | |
357 ############################################################################### |