blob: a9373ed09f9da803f4d522b283bdcc1db5524715 [file] [log] [blame]
Bradford Wetmorea1ebe172016-06-08 11:28:13 -07001/*
2 * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24// SunJSSE does not support dynamic system properties, no way to re-use
25// system properties in samevm/agentvm mode.
26
27/*
28 * @test
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +000029 * @bug 8051498 8145849 8158978 8170282
Bradford Wetmorea1ebe172016-06-08 11:28:13 -070030 * @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
31 * @compile MyX509ExtendedKeyManager.java
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +000032 *
33 * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2 h2
34 * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2,http/1.1 h2
35 * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
36 * @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
37 * @run main/othervm SSLServerSocketAlpnTest h4,h3,h2 UNUSED h1,h2 h2
38 * @run main/othervm SSLServerSocketAlpnTest EMPTY UNUSED h2,http/1.1 NONE
39 * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED EMPTY NONE
40 * @run main/othervm SSLServerSocketAlpnTest H2 UNUSED h2 ERROR
41 * @run main/othervm SSLServerSocketAlpnTest h2 UNUSED http/1.1 ERROR
42 *
43 * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2 h2
44 * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2,http/1.1 h2
45 * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1,h2 h2
46 * @run main/othervm SSLServerSocketAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
47 * @run main/othervm SSLServerSocketAlpnTest UNUSED EMPTY h2,http/1.1 NONE
48 * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 EMPTY NONE
49 * @run main/othervm SSLServerSocketAlpnTest UNUSED H2 h2 ERROR
50 * @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1 ERROR
51 *
52 * @run main/othervm SSLServerSocketAlpnTest h2 h2 h2 h2
53 * @run main/othervm SSLServerSocketAlpnTest H2 h2 h2,http/1.1 h2
54 * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
55 * @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
56 * @run main/othervm SSLServerSocketAlpnTest EMPTY h2 h2 h2
57 * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
58 * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 h2 EMPTY NONE
59 * @run main/othervm SSLServerSocketAlpnTest UNUSED UNUSED http/1.1,h2 NONE
60 * @run main/othervm SSLServerSocketAlpnTest h2 h2 http/1.1 ERROR
61 * @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 H2 http/1.1 ERROR
62 *
Bradford Wetmorea1ebe172016-06-08 11:28:13 -070063 * @author Brad Wetmore
64 */
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +000065/**
66 * A simple SSLSocket-based client/server that demonstrates the proposed API
67 * changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
68 *
69 * Usage:
70 * java SSLServerSocketAlpnTest
71 * <server-APs> <callback-AP> <client-APs> <result>
72 *
73 * where:
74 * EMPTY indicates that ALPN is disabled
75 * UNUSED indicates that no ALPN values are supplied (server-side only)
76 * ERROR indicates that an exception is expected
77 * NONE indicates that no ALPN is expected
78 *
79 * This example is based on our standard SSLSocketTemplate.
80 */
Bradford Wetmorea1ebe172016-06-08 11:28:13 -070081import java.io.*;
82import java.security.KeyStore;
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +000083import java.util.Arrays;
Bradford Wetmorea1ebe172016-06-08 11:28:13 -070084
85import javax.net.ssl.*;
86
87public class SSLServerSocketAlpnTest {
88
89 /*
90 * =============================================================
91 * Set the various variables needed for the tests, then
92 * specify what tests to run on each side.
93 */
94
95 /*
96 * Should we run the client or server in a separate thread?
97 * Both sides can throw exceptions, but do you have a preference
98 * as to which side should be the main thread.
99 */
100 static boolean separateServerThread = false;
101
102 /*
103 * Where do we find the keystores?
104 */
105 static String pathToStores = "../etc";
106 static String keyStoreFile = "keystore";
107 static String trustStoreFile = "truststore";
108 static String passwd = "passphrase";
109
110 static String keyFilename = System.getProperty("test.src", ".") + "/"
111 + pathToStores + "/" + keyStoreFile;
112 static String trustFilename = System.getProperty("test.src", ".") + "/"
113 + pathToStores + "/" + trustStoreFile;
114
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000115 private static boolean hasServerAPs; // whether server APs are present
116 private static boolean hasCallback; // whether a callback is present
117
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700118 /*
119 * SSLContext
120 */
121 SSLContext mySSLContext = null;
122
123 /*
124 * Is the server ready to serve?
125 */
126 volatile static boolean serverReady = false;
127
128 /*
129 * Turn on SSL debugging?
130 */
131 static boolean debug = false;
132
133 static String[] serverAPs;
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000134 static String callbackAP;
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700135 static String[] clientAPs;
136 static String expectedAP;
137
138 /*
139 * If the client or server is doing some kind of object creation
140 * that the other side depends on, and that thread prematurely
141 * exits, you may experience a hang. The test harness will
142 * terminate all hung threads after its timeout has expired,
143 * currently 3 minutes by default, but you might try to be
144 * smart about it....
145 */
146
147 /*
148 * Define the server side of the test.
149 *
150 * If the server prematurely exits, serverReady will be set to true
151 * to avoid infinite hangs.
152 */
153 void doServerSide() throws Exception {
154 SSLServerSocketFactory sslssf = mySSLContext.getServerSocketFactory();
155 SSLServerSocket sslServerSocket
156 = (SSLServerSocket) sslssf.createServerSocket(serverPort);
157 sslServerSocket.setNeedClientAuth(true);
158
159 SSLParameters sslp = sslServerSocket.getSSLParameters();
160
161 // for both client/server to call into X509KM
162 sslp.setNeedClientAuth(true);
163
164 /*
165 * The default ciphersuite ordering from the SSLContext may not
166 * reflect "h2" ciphersuites as being preferred, additionally the
167 * client may not send them in an appropriate order. We could resort
168 * the suite list if so desired.
169 */
170 String[] suites = sslp.getCipherSuites();
171 sslp.setCipherSuites(suites);
172 sslp.setUseCipherSuitesOrder(true); // Set server side order
173
174 // Set the ALPN selection.
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000175 if (serverAPs != null) {
176 sslp.setApplicationProtocols(serverAPs);
177 }
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700178 sslServerSocket.setSSLParameters(sslp);
179
180 serverPort = sslServerSocket.getLocalPort();
181
182 /*
183 * Signal Client, we're ready for his connect.
184 */
185 serverReady = true;
186
187 SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
188
189 if (sslSocket.getHandshakeApplicationProtocol() != null) {
190 throw new Exception ("getHandshakeApplicationProtocol() should "
191 + "return null before the handshake starts");
192 }
193
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000194 // check that no callback has been registered
195 if (sslSocket.getHandshakeApplicationProtocolSelector() != null) {
196 throw new Exception("getHandshakeApplicationProtocolSelector() " +
197 "should return null");
198 }
199
200 if (hasCallback) {
201 sslSocket.setHandshakeApplicationProtocolSelector(
202 (serverSocket, clientProtocols) -> {
203 return callbackAP.equals("EMPTY") ? "" : callbackAP;
204 });
205
206 // check that the callback can be retrieved
207 if (sslSocket.getHandshakeApplicationProtocolSelector() == null) {
208 throw new Exception("getHandshakeApplicationProtocolSelector()"
209 + " should return non-null");
210 }
211 }
212
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700213 sslSocket.startHandshake();
214
215 if (sslSocket.getHandshakeApplicationProtocol() != null) {
216 throw new Exception ("getHandshakeApplicationProtocol() should "
217 + "return null after the handshake is completed");
218 }
219
220 String ap = sslSocket.getApplicationProtocol();
221 System.out.println("Application Protocol: \"" + ap + "\"");
222
223 if (ap == null) {
224 throw new Exception(
225 "Handshake was completed but null was received");
226 }
227 if (expectedAP.equals("NONE")) {
228 if (!ap.isEmpty()) {
229 throw new Exception("Expected no ALPN value");
230 } else {
231 System.out.println("No ALPN value negotiated, as expected");
232 }
233 } else if (!expectedAP.equals(ap)) {
234 throw new Exception(expectedAP
235 + " ALPN value not available on negotiated connection");
236 }
237
238 InputStream sslIS = sslSocket.getInputStream();
239 OutputStream sslOS = sslSocket.getOutputStream();
240
241 sslIS.read();
242 sslOS.write(85);
243 sslOS.flush();
244
245 sslSocket.close();
246 }
247
248 /*
249 * Define the client side of the test.
250 *
251 * If the server prematurely exits, serverReady will be set to true
252 * to avoid infinite hangs.
253 */
254 void doClientSide() throws Exception {
255
256 /*
257 * Wait for server to get started.
258 */
259 while (!serverReady) {
260 Thread.sleep(50);
261 }
262
263 SSLSocketFactory sslsf = mySSLContext.getSocketFactory();
264 SSLSocket sslSocket
265 = (SSLSocket) sslsf.createSocket("localhost", serverPort);
266
267 SSLParameters sslp = sslSocket.getSSLParameters();
268
269 /*
270 * The default ciphersuite ordering from the SSLContext may not
271 * reflect "h2" ciphersuites as being preferred, additionally the
272 * client may not send them in an appropriate order. We could resort
273 * the suite list if so desired.
274 */
275 String[] suites = sslp.getCipherSuites();
276 sslp.setCipherSuites(suites);
277 sslp.setUseCipherSuitesOrder(true); // Set server side order
278
279 // Set the ALPN selection.
280 sslp.setApplicationProtocols(clientAPs);
281 sslSocket.setSSLParameters(sslp);
282
283 if (sslSocket.getHandshakeApplicationProtocol() != null) {
284 throw new Exception ("getHandshakeApplicationProtocol() should "
285 + "return null before the handshake starts");
286 }
287
288 sslSocket.startHandshake();
289
290 if (sslSocket.getHandshakeApplicationProtocol() != null) {
291 throw new Exception ("getHandshakeApplicationProtocol() should "
292 + "return null after the handshake is completed");
293 }
294
295 /*
296 * Check that the resulting connection meets our defined ALPN
297 * criteria. If we were connecting to a non-JSSE implementation,
298 * the server might have negotiated something we shouldn't accept.
299 */
300 String ap = sslSocket.getApplicationProtocol();
301 System.out.println("Application Protocol: \"" + ap + "\"");
302
303 if (ap == null) {
304 throw new Exception(
305 "Handshake was completed but null was received");
306 }
307 if (expectedAP.equals("NONE")) {
308 if (!ap.isEmpty()) {
309 throw new Exception("Expected no ALPN value");
310 } else {
311 System.out.println("No ALPN value negotiated, as expected");
312 }
313 } else if (!expectedAP.equals(ap)) {
314 throw new Exception(expectedAP
315 + " ALPN value not available on negotiated connection");
316 }
317
318 InputStream sslIS = sslSocket.getInputStream();
319 OutputStream sslOS = sslSocket.getOutputStream();
320
321 sslOS.write(280);
322 sslOS.flush();
323 sslIS.read();
324
325 sslSocket.close();
326 }
327
328 /*
329 * =============================================================
330 * The remainder is just support stuff
331 */
332 // use any free port by default
333 volatile int serverPort = 0;
334
335 volatile Exception serverException = null;
336 volatile Exception clientException = null;
337
338 public static void main(String[] args) throws Exception {
339
340 if (debug) {
341 System.setProperty("javax.net.debug", "all");
342 }
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000343 System.out.println("Test args: " + Arrays.toString(args));
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700344
345 // Validate parameters
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000346 if (args.length != 4) {
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700347 throw new Exception("Invalid number of test parameters");
348 }
349 serverAPs = convert(args[0]);
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000350 callbackAP = args[1];
351 clientAPs = convert(args[2]);
352 expectedAP = args[3];
353
354 hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
355 hasCallback = !callbackAP.equals("UNUSED"); // is callback being used?
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700356
357 /*
358 * Start the tests.
359 */
360 try {
361 new SSLServerSocketAlpnTest();
362 } catch (SSLHandshakeException she) {
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000363 if (args[3].equals("ERROR")) {
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700364 System.out.println("Caught the expected exception: " + she);
365 } else {
366 throw she;
367 }
368 }
369
370 System.out.println("Test Passed.");
371 }
372
373 SSLContext getSSLContext(String keyFilename, String trustFilename)
374 throws Exception {
375 SSLContext ctx = SSLContext.getInstance("TLS");
376
377 // Keystores
378 KeyStore keyKS = KeyStore.getInstance("JKS");
379 keyKS.load(new FileInputStream(keyFilename), passwd.toCharArray());
380
381 KeyStore trustKS = KeyStore.getInstance("JKS");
382 trustKS.load(new FileInputStream(trustFilename), passwd.toCharArray());
383
384 // Generate KeyManager and TrustManager
385 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
386 kmf.init(keyKS, passwd.toCharArray());
387
388 KeyManager[] kms = kmf.getKeyManagers();
389 if (!(kms[0] instanceof X509ExtendedKeyManager)) {
390 throw new Exception("kms[0] not X509ExtendedKeyManager");
391 }
392
393 kms = new KeyManager[] { new MyX509ExtendedKeyManager(
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000394 (X509ExtendedKeyManager) kms[0], expectedAP,
395 !hasCallback && hasServerAPs) };
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700396
397 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
398 tmf.init(trustKS);
399 TrustManager[] tms = tmf.getTrustManagers();
400
401 // initial SSLContext
402 ctx.init(kms, tms, null);
403
404 return ctx;
405 }
406
407 /*
408 * Convert a comma-separated list into an array of strings.
409 */
410 private static String[] convert(String list) {
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000411 if (list.equals("UNUSED")) {
412 return null;
413 }
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700414
415 if (list.equals("EMPTY")) {
416 return new String[0];
417 }
418
Vinnie Ryanbd0a13f2016-12-16 14:32:51 +0000419 String[] strings;
Bradford Wetmorea1ebe172016-06-08 11:28:13 -0700420 if (list.indexOf(',') > 0) {
421 strings = list.split(",");
422 } else {
423 strings = new String[]{ list };
424 }
425
426 return strings;
427 }
428
429 Thread clientThread = null;
430 Thread serverThread = null;
431
432 /*
433 * Primary constructor, used to drive remainder of the test.
434 *
435 * Fork off the other side, then do your work.
436 */
437 SSLServerSocketAlpnTest() throws Exception {
438 Exception startException = null;
439 mySSLContext = getSSLContext(keyFilename, trustFilename);
440 try {
441 if (separateServerThread) {
442 startServer(true);
443 startClient(false);
444 } else {
445 startClient(true);
446 startServer(false);
447 }
448 } catch (Exception e) {
449 startException = e;
450 }
451
452 /*
453 * Wait for other side to close down.
454 */
455 if (separateServerThread) {
456 if (serverThread != null) {
457 serverThread.join();
458 }
459 } else {
460 if (clientThread != null) {
461 clientThread.join();
462 }
463 }
464
465 /*
466 * When we get here, the test is pretty much over.
467 * Which side threw the error?
468 */
469 Exception local;
470 Exception remote;
471
472 if (separateServerThread) {
473 remote = serverException;
474 local = clientException;
475 } else {
476 remote = clientException;
477 local = serverException;
478 }
479
480 Exception exception = null;
481
482 /*
483 * Check various exception conditions.
484 */
485 if ((local != null) && (remote != null)) {
486 // If both failed, return the curthread's exception.
487 local.initCause(remote);
488 exception = local;
489 } else if (local != null) {
490 exception = local;
491 } else if (remote != null) {
492 exception = remote;
493 } else if (startException != null) {
494 exception = startException;
495 }
496
497 /*
498 * If there was an exception *AND* a startException,
499 * output it.
500 */
501 if (exception != null) {
502 if (exception != startException && startException != null) {
503 exception.addSuppressed(startException);
504 }
505 throw exception;
506 }
507
508 // Fall-through: no exception to throw!
509 }
510
511 void startServer(boolean newThread) throws Exception {
512 if (newThread) {
513 serverThread = new Thread() {
514 @Override
515 public void run() {
516 try {
517 doServerSide();
518 } catch (Exception e) {
519 /*
520 * Our server thread just died.
521 *
522 * Release the client, if not active already...
523 */
524 System.err.println("Server died...");
525 serverReady = true;
526 serverException = e;
527 }
528 }
529 };
530 serverThread.start();
531 } else {
532 try {
533 doServerSide();
534 } catch (Exception e) {
535 serverException = e;
536 } finally {
537 serverReady = true;
538 }
539 }
540 }
541
542 void startClient(boolean newThread) throws Exception {
543 if (newThread) {
544 clientThread = new Thread() {
545 @Override
546 public void run() {
547 try {
548 doClientSide();
549 } catch (Exception e) {
550 /*
551 * Our client thread just died.
552 */
553 System.err.println("Client died...");
554 clientException = e;
555 }
556 }
557 };
558 clientThread.start();
559 } else {
560 try {
561 doClientSide();
562 } catch (Exception e) {
563 clientException = e;
564 }
565 }
566 }
567}