1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.ssl;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.InetSocketAddress;
34 import java.net.ServerSocket;
35 import java.net.Socket;
36 import java.net.URL;
37 import java.security.KeyStore;
38 import java.security.KeyStoreException;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.NoSuchProviderException;
41 import java.security.Principal;
42 import java.security.Security;
43 import java.security.UnrecoverableKeyException;
44 import java.security.cert.X509Certificate;
45 import java.util.Arrays;
46 import java.util.LinkedHashSet;
47 import java.util.Set;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.Executors;
50 import java.util.concurrent.Future;
51 import java.util.concurrent.TimeUnit;
52 import java.util.concurrent.atomic.AtomicReference;
53
54 import javax.net.ssl.KeyManagerFactory;
55 import javax.net.ssl.SSLContext;
56 import javax.net.ssl.SSLException;
57 import javax.net.ssl.SSLParameters;
58 import javax.net.ssl.SSLPeerUnverifiedException;
59 import javax.net.ssl.SSLServerSocket;
60 import javax.net.ssl.SSLSession;
61 import javax.net.ssl.SSLSocket;
62 import javax.net.ssl.TrustManagerFactory;
63
64 import org.apache.hc.core5.util.Timeout;
65 import org.junit.jupiter.api.AfterEach;
66 import org.junit.jupiter.api.Assertions;
67 import org.junit.jupiter.api.Test;
68
69
70
71
72 public class TestSSLContextBuilder {
73
74 static final String PROVIDER_SUN_JSSE = "SunJSSE";
75 static final String PROVIDER_SUN_JCE = "SunJCE";
76
77 private static boolean isWindows() {
78 return System.getProperty("os.name").contains("Windows");
79 }
80
81 private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
82 private ExecutorService executorService;
83
84 @AfterEach
85 public void cleanup() throws Exception {
86 if (this.executorService != null) {
87 this.executorService.shutdown();
88 this.executorService.awaitTermination(5, TimeUnit.SECONDS);
89 }
90 }
91
92 private URL getResource(final String name) {
93 return getClass().getResource(name);
94 }
95
96 @Test
97 public void testBuildAllDefaults() throws Exception {
98 final SSLContext sslContext = SSLContextBuilder.create()
99 .setKeyStoreType(KeyStore.getDefaultType())
100 .setKeyManagerFactoryAlgorithm(KeyManagerFactory.getDefaultAlgorithm())
101 .setTrustManagerFactoryAlgorithm(TrustManagerFactory.getDefaultAlgorithm())
102 .setProvider(PROVIDER_SUN_JSSE)
103 .setProtocol("TLS")
104 .setSecureRandom(null)
105 .loadTrustMaterial((KeyStore) null, null)
106 .loadKeyMaterial((KeyStore) null, null, null)
107 .build();
108 Assertions.assertNotNull(sslContext);
109 Assertions.assertEquals("TLS", sslContext.getProtocol());
110 Assertions.assertEquals(PROVIDER_SUN_JSSE, sslContext.getProvider().getName());
111 }
112
113 @Test
114 public void testBuildAllNull() throws Exception {
115 final SSLContext sslContext = SSLContextBuilder.create()
116 .setKeyStoreType(null)
117 .setKeyManagerFactoryAlgorithm(null)
118 .setTrustManagerFactoryAlgorithm(null)
119 .setProtocol(null)
120 .setProvider((String) null)
121 .setSecureRandom(null)
122 .loadTrustMaterial((KeyStore) null, null)
123 .loadKeyMaterial((KeyStore) null, null, null)
124 .build();
125 Assertions.assertNotNull(sslContext);
126 Assertions.assertEquals("TLS", sslContext.getProtocol());
127 Assertions.assertEquals(PROVIDER_SUN_JSSE, sslContext.getProvider().getName());
128 }
129
130 @Test
131 public void testBuildAllNull_deprecated() throws Exception {
132 final SSLContext sslContext = SSLContextBuilder.create()
133 .setProtocol(null)
134 .setSecureRandom(null)
135 .loadTrustMaterial((KeyStore) null, null)
136 .loadKeyMaterial((KeyStore) null, null, null)
137 .build();
138 Assertions.assertNotNull(sslContext);
139 Assertions.assertEquals("TLS", sslContext.getProtocol());
140 }
141
142 @Test
143 public void testBuildDefault() throws Exception {
144 new SSLContextBuilder().build();
145 }
146
147 @Test
148 public void testBuildNoSuchKeyManagerFactoryAlgorithm() throws Exception {
149 final URL resource1 = getResource("/test-keypasswd.p12");
150 final String storePassword = "nopassword";
151 final String keyPassword = "password";
152 Assertions.assertThrows(NoSuchAlgorithmException.class, () ->
153 SSLContextBuilder.create()
154 .setKeyManagerFactoryAlgorithm(" BAD ")
155 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
156 .build());
157 }
158
159 @Test
160 public void testBuildNoSuchKeyStoreType() throws Exception {
161 final URL resource1 = getResource("/test-keypasswd.p12");
162 final String storePassword = "nopassword";
163 final String keyPassword = "password";
164 Assertions.assertThrows(KeyStoreException.class, () ->
165 SSLContextBuilder.create()
166 .setKeyStoreType(" BAD ")
167 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
168 .build());
169 }
170
171 @Test
172 public void testBuildNoSuchTrustManagerFactoryAlgorithm() throws Exception {
173 final URL resource1 = getResource("/test-keypasswd.p12");
174 final String storePassword = "nopassword";
175 Assertions.assertThrows(NoSuchAlgorithmException.class, () ->
176 SSLContextBuilder.create()
177 .setTrustManagerFactoryAlgorithm(" BAD ")
178 .loadTrustMaterial(resource1, storePassword.toCharArray())
179 .build());
180 }
181
182 @Test
183 public void testBuildWithProvider() throws Exception {
184 final URL resource1 = getResource("/test-server.p12");
185 final String storePassword = "nopassword";
186 final String keyPassword = "nopassword";
187 final DummyProvider provider = new DummyProvider();
188 SSLContextBuilder.create()
189 .setProvider(provider)
190 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
191 .build();
192 Assertions.assertTrue(provider.hasBeenRequested("SSLContext"));
193 }
194
195 @Test
196 public void testBuildWithProviderName() throws Exception {
197
198 final DummyProvider provider = new DummyProvider();
199 Security.insertProviderAt(provider, 1);
200 try {
201
202 final URL resource1 = getResource("/test-server.p12");
203 final String storePassword = "nopassword";
204 final String keyPassword = "nopassword";
205 SSLContextBuilder.create()
206 .setProvider(DummyProvider.NAME)
207 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
208 .build();
209 Assertions.assertTrue(provider.hasBeenRequested("SSLContext"));
210
211 } finally {
212 Security.removeProvider(DummyProvider.NAME);
213 }
214 }
215
216 @Test
217 public void testBuildKSWithNoSuchProvider() {
218 Assertions.assertThrows(NoSuchProviderException.class,
219 () -> SSLContextBuilder.create()
220 .setKeyStoreProvider("no-such-provider")
221 .build());
222 }
223
224 @Test
225 public void testBuildKSWithProvider() throws Exception {
226 final URL resource1 = getResource("/test-server.p12");
227 final String storePassword = "nopassword";
228 final String keyPassword = "nopassword";
229 final DummyProvider provider = new DummyProvider();
230 SSLContextBuilder.create()
231 .setKeyStoreProvider(provider)
232 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
233 .build();
234 Assertions.assertTrue(provider.hasBeenRequested("KeyManagerFactory"));
235 }
236
237 @Test
238 public void testBuildKSWithProviderName() throws Exception {
239
240 final DummyProvider provider = new DummyProvider();
241 Security.insertProviderAt(provider, 1);
242 try {
243
244 final URL resource1 = getResource("/test-server.p12");
245 final String storePassword = "nopassword";
246 final String keyPassword = "nopassword";
247 SSLContextBuilder.create()
248 .setKeyStoreProvider(DummyProvider.NAME)
249 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
250 .build();
251 Assertions.assertTrue(provider.hasBeenRequested("KeyManagerFactory"));
252
253 } finally {
254 Security.removeProvider(DummyProvider.NAME);
255 }
256 }
257
258 @Test
259 public void testBuildTSWithNoSuchProvider() {
260 Assertions.assertThrows(NoSuchProviderException.class, ()->
261 SSLContextBuilder.create()
262 .setTrustStoreProvider("no-such-provider")
263 .build());
264 }
265
266 @Test
267 public void testBuildTSWithProvider() throws Exception {
268 final DummyProvider provider = new DummyProvider();
269 SSLContextBuilder.create()
270 .setTrustStoreProvider(provider)
271 .loadTrustMaterial((KeyStore) null, null)
272 .build();
273 Assertions.assertTrue(provider.hasBeenRequested("TrustManagerFactory"));
274 }
275
276 @Test
277 public void testBuildTSWithProviderName() throws Exception {
278
279 final DummyProvider provider = new DummyProvider();
280 Security.insertProviderAt(provider, 1);
281 try {
282
283 SSLContextBuilder.create()
284 .setTrustStoreProvider(DummyProvider.NAME)
285 .loadTrustMaterial((KeyStore) null, null)
286 .build();
287 Assertions.assertTrue(provider.hasBeenRequested("TrustManagerFactory"));
288
289 } finally {
290 Security.removeProvider(DummyProvider.NAME);
291 }
292 }
293
294
295 @Test
296 public void testKeyWithAlternatePasswordInvalid() throws Exception {
297 final URL resource1 = getResource("/test-keypasswd.p12");
298 final String storePassword = "nopassword";
299 final String keyPassword = "!password";
300 Assertions.assertThrows(UnrecoverableKeyException.class, () ->
301 SSLContextBuilder.create()
302 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
303 .loadTrustMaterial(resource1, storePassword.toCharArray())
304 .build());
305 }
306
307 @Test
308 public void testSSLHandshakeServerTrusted() throws Exception {
309 final URL resource1 = getResource("/test.p12");
310 final String storePassword = "nopassword";
311 final String keyPassword = "nopassword";
312 final SSLContext serverSslContext = SSLContextBuilder.create()
313 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
314 .build();
315 Assertions.assertNotNull(serverSslContext);
316 final SSLContext clientSslContext = SSLContextBuilder.create()
317 .loadTrustMaterial(resource1, storePassword.toCharArray())
318 .build();
319 Assertions.assertNotNull(clientSslContext);
320 final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
321 serverSocket.bind(new InetSocketAddress(0));
322
323 this.executorService = Executors.newSingleThreadExecutor();
324 final Future<Boolean> future = this.executorService.submit(() -> {
325 try (Socket socket = serverSocket.accept()) {
326 final OutputStream outputStream = socket.getOutputStream();
327 outputStream.write(new byte[]{'H', 'i'});
328 outputStream.flush();
329 }
330 return Boolean.TRUE;
331 });
332
333 final int localPort = serverSocket.getLocalPort();
334 try (final Socket clientSocket = clientSslContext.getSocketFactory().createSocket()) {
335 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
336 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
337 final InputStream inputStream = clientSocket.getInputStream();
338 Assertions.assertEquals('H', inputStream.read());
339 Assertions.assertEquals('i', inputStream.read());
340 Assertions.assertEquals(-1, inputStream.read());
341 }
342
343 final Boolean result = future.get(5, TimeUnit.SECONDS);
344 Assertions.assertNotNull(result);
345 }
346
347 @Test
348 public void testSSLHandshakeServerNotTrusted() throws Exception {
349 final URL resource1 = getResource("/test-server.p12");
350 final String storePassword = "nopassword";
351 final String keyPassword = "nopassword";
352 final SSLContext serverSslContext = SSLContextBuilder.create()
353 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
354 .build();
355 Assertions.assertNotNull(serverSslContext);
356 final URL resource2 = getResource("/test.p12");
357 final SSLContext clientSslContext = SSLContextBuilder.create()
358 .loadTrustMaterial(resource2, storePassword.toCharArray())
359 .build();
360 Assertions.assertNotNull(clientSslContext);
361 final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
362 serverSocket.bind(new InetSocketAddress(0));
363
364 this.executorService = Executors.newSingleThreadExecutor();
365 this.executorService.submit(() -> {
366 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
367 socket.getSession();
368 }
369 return Boolean.FALSE;
370 });
371 final int localPort = serverSocket.getLocalPort();
372 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
373 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
374 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
375 Assertions.assertThrows(IOException.class, clientSocket::startHandshake);
376 }
377 }
378
379 @Test
380 public void testSSLHandshakeServerCustomTrustStrategy() throws Exception {
381 final URL resource1 = getResource("/test-server.p12");
382 final String storePassword = "nopassword";
383 final String keyPassword = "nopassword";
384 final SSLContext serverSslContext = SSLContextBuilder.create()
385 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
386 .build();
387 Assertions.assertNotNull(serverSslContext);
388
389 final AtomicReference<X509Certificate[]> certChainRef = new AtomicReference<>();
390
391 final TrustStrategy trustStrategy = (chain, authType) -> {
392 certChainRef.set(chain);
393 return true;
394 };
395
396 final SSLContext clientSslContext = SSLContextBuilder.create()
397 .loadTrustMaterial(trustStrategy)
398 .build();
399
400 Assertions.assertNotNull(clientSslContext);
401 final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
402 serverSocket.bind(new InetSocketAddress(0));
403
404 this.executorService = Executors.newSingleThreadExecutor();
405 final Future<Boolean> future = this.executorService.submit(() -> {
406 try (Socket socket = serverSocket.accept()) {
407 final OutputStream outputStream = socket.getOutputStream();
408 outputStream.write(new byte[]{'H', 'i'});
409 outputStream.flush();
410 }
411 return Boolean.TRUE;
412 });
413
414 final int localPort = serverSocket.getLocalPort();
415 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
416 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
417 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
418 final InputStream inputStream = clientSocket.getInputStream();
419 Assertions.assertEquals('H', inputStream.read());
420 Assertions.assertEquals('i', inputStream.read());
421 Assertions.assertEquals(-1, inputStream.read());
422 }
423
424 final Boolean result = future.get(5, TimeUnit.SECONDS);
425 Assertions.assertNotNull(result);
426
427 final X509Certificate[] certs = certChainRef.get();
428 Assertions.assertNotNull(certs);
429 Assertions.assertEquals(2, certs.length);
430 final X509Certificate cert1 = certs[0];
431 final Principal subjectDN1 = cert1.getSubjectDN();
432 Assertions.assertNotNull(subjectDN1);
433 Assertions.assertEquals("CN=Test Server, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN1.getName());
434 final X509Certificate cert2 = certs[1];
435 final Principal subjectDN2 = cert2.getSubjectDN();
436 Assertions.assertNotNull(subjectDN2);
437 Assertions.assertEquals("EMAILADDRESS=dev@hc.apache.org, " +
438 "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN2.getName());
439 final Principal issuerDN = cert2.getIssuerDN();
440 Assertions.assertNotNull(issuerDN);
441 Assertions.assertEquals("EMAILADDRESS=dev@hc.apache.org, " +
442 "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", issuerDN.getName());
443
444 }
445
446 @Test
447 public void testSSLHandshakeClientUnauthenticated() throws Exception {
448 final URL resource1 = getResource("/test-server.p12");
449 final String storePassword = "nopassword";
450 final String keyPassword = "nopassword";
451 final SSLContext serverSslContext = SSLContextBuilder.create()
452 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
453 .build();
454 Assertions.assertNotNull(serverSslContext);
455 final URL resource2 = getResource("/test-client.p12");
456 final SSLContext clientSslContext = SSLContextBuilder.create()
457 .loadTrustMaterial(resource2, storePassword.toCharArray())
458 .build();
459 Assertions.assertNotNull(clientSslContext);
460 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
461 serverSocket.setWantClientAuth(true);
462 serverSocket.bind(new InetSocketAddress(0));
463
464 this.executorService = Executors.newSingleThreadExecutor();
465 final Future<Principal> future = this.executorService.submit(() -> {
466 final SSLSocket socket = (SSLSocket) serverSocket.accept();
467 Principal clientPrincipal = null;
468 try {
469 final SSLSession session = socket.getSession();
470 try {
471 clientPrincipal = session.getPeerPrincipal();
472 } catch (final SSLPeerUnverifiedException ignore) {
473 }
474 final OutputStream outputStream = socket.getOutputStream();
475 outputStream.write(new byte [] {'H', 'i'});
476 outputStream.flush();
477 } finally {
478 socket.close();
479 }
480 return clientPrincipal;
481 });
482
483 final int localPort = serverSocket.getLocalPort();
484 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
485 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
486 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
487 clientSocket.startHandshake();
488 final InputStream inputStream = clientSocket.getInputStream();
489 Assertions.assertEquals('H', inputStream.read());
490 Assertions.assertEquals('i', inputStream.read());
491 Assertions.assertEquals(-1, inputStream.read());
492 }
493
494 final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
495 Assertions.assertNull(clientPrincipal);
496 }
497
498 @Test
499 public void testSSLHandshakeClientUnauthenticatedError() throws Exception {
500 final URL resource1 = getResource("/test-server.p12");
501 final String storePassword = "nopassword";
502 final String keyPassword = "nopassword";
503 final SSLContext serverSslContext = SSLContextBuilder.create()
504 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
505 .build();
506 Assertions.assertNotNull(serverSslContext);
507 final URL resource2 = getResource("/test-client.p12");
508 final SSLContext clientSslContext = SSLContextBuilder.create()
509 .loadTrustMaterial(resource2, storePassword.toCharArray())
510 .build();
511 Assertions.assertNotNull(clientSslContext);
512 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
513 serverSocket.setNeedClientAuth(true);
514 serverSocket.bind(new InetSocketAddress(0));
515
516 this.executorService = Executors.newSingleThreadExecutor();
517 this.executorService.submit(() -> {
518 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
519 socket.getSession();
520 }
521 return Boolean.FALSE;
522 });
523
524 final int localPort = serverSocket.getLocalPort();
525 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
526 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
527 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
528 Assertions.assertThrows(IOException.class, () -> {
529 clientSocket.startHandshake();
530 final InputStream inputStream = clientSocket.getInputStream();
531 inputStream.read();
532 });
533 }
534 }
535
536 @Test
537 public void testSSLHandshakeClientAuthenticated() throws Exception {
538 final URL resource1 = getResource("/test-server.p12");
539 final String storePassword = "nopassword";
540 final String keyPassword = "nopassword";
541 final SSLContext serverSslContext = SSLContextBuilder.create()
542 .loadTrustMaterial(resource1, storePassword.toCharArray())
543 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
544 .build();
545 Assertions.assertNotNull(serverSslContext);
546 final URL resource2 = getResource("/test-client.p12");
547 final SSLContext clientSslContext = SSLContextBuilder.create()
548 .loadTrustMaterial(resource2, storePassword.toCharArray())
549 .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray())
550 .build();
551 Assertions.assertNotNull(clientSslContext);
552 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
553 serverSocket.setNeedClientAuth(true);
554 serverSocket.bind(new InetSocketAddress(0));
555
556 this.executorService = Executors.newSingleThreadExecutor();
557 final Future<Principal> future = this.executorService.submit(() -> {
558 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
559 final SSLSession session = socket.getSession();
560 final Principal clientPrincipal = session.getPeerPrincipal();
561 final OutputStream outputStream = socket.getOutputStream();
562 outputStream.write(new byte[]{'H', 'i'});
563 outputStream.flush();
564 return clientPrincipal;
565 }
566 });
567 final int localPort = serverSocket.getLocalPort();
568 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
569 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
570 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
571 clientSocket.startHandshake();
572 final InputStream inputStream = clientSocket.getInputStream();
573 Assertions.assertEquals('H', inputStream.read());
574 Assertions.assertEquals('i', inputStream.read());
575 Assertions.assertEquals(-1, inputStream.read());
576 }
577
578 final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
579 Assertions.assertNotNull(clientPrincipal);
580 }
581
582 @Test
583 public void testSSLHandshakeClientAuthenticatedPrivateKeyStrategy() throws Exception {
584 final URL resource1 = getResource("/test-server.p12");
585 final String storePassword = "nopassword";
586 final String keyPassword = "nopassword";
587 final SSLContext serverSslContext = SSLContextBuilder.create()
588 .loadTrustMaterial(resource1, storePassword.toCharArray())
589 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
590 .build();
591 Assertions.assertNotNull(serverSslContext);
592
593 final PrivateKeyStrategy privateKeyStrategy = (aliases, sslParameters) -> aliases.containsKey("client2") ? "client2" : null;
594
595 final URL resource2 = getResource("/test-client.p12");
596 final SSLContext clientSslContext = SSLContextBuilder.create()
597 .loadTrustMaterial(resource2, storePassword.toCharArray())
598 .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray(), privateKeyStrategy)
599 .build();
600 Assertions.assertNotNull(clientSslContext);
601 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
602 serverSocket.setNeedClientAuth(true);
603 serverSocket.bind(new InetSocketAddress(0));
604
605 this.executorService = Executors.newSingleThreadExecutor();
606 final Future<Principal> future = this.executorService.submit(() -> {
607 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
608 final SSLSession session = socket.getSession();
609 final Principal clientPrincipal = session.getPeerPrincipal();
610 final OutputStream outputStream = socket.getOutputStream();
611 outputStream.write(new byte[]{'H', 'i'});
612 outputStream.flush();
613 return clientPrincipal;
614 }
615 });
616 final int localPort = serverSocket.getLocalPort();
617 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
618 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
619 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
620 clientSocket.startHandshake();
621 final InputStream inputStream = clientSocket.getInputStream();
622 Assertions.assertEquals('H', inputStream.read());
623 Assertions.assertEquals('i', inputStream.read());
624 Assertions.assertEquals(-1, inputStream.read());
625 }
626
627 final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
628 Assertions.assertNotNull(clientPrincipal);
629 Assertions.assertEquals("CN=Test Client 2,OU=HttpComponents Project,O=Apache Software Foundation", clientPrincipal.getName());
630 }
631
632
633 @Test
634 public void testSSLHandshakeProtocolMismatch1() throws Exception {
635 final URL resource1 = getResource("/test-server.p12");
636 final String storePassword = "nopassword";
637 final String keyPassword = "nopassword";
638 final SSLContext serverSslContext = SSLContextBuilder.create()
639 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
640 .build();
641 Assertions.assertNotNull(serverSslContext);
642 final URL resource2 = getResource("/test-client.p12");
643 final SSLContext clientSslContext = SSLContextBuilder.create()
644 .loadTrustMaterial(resource2, storePassword.toCharArray())
645 .build();
646 Assertions.assertNotNull(clientSslContext);
647 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
648 final Set<String> supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols()));
649 Assertions.assertTrue(supportedServerProtocols.contains("TLSv1"));
650 serverSocket.setEnabledProtocols(new String[] {"TLSv1"});
651 serverSocket.bind(new InetSocketAddress(0));
652
653 this.executorService = Executors.newSingleThreadExecutor();
654 this.executorService.submit(() -> {
655 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
656 socket.getSession();
657 }
658 return Boolean.FALSE;
659 });
660
661 final int localPort = serverSocket.getLocalPort();
662 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
663 final Set<String> supportedClientProtocols = new LinkedHashSet<>(Arrays.asList(clientSocket.getSupportedProtocols()));
664 Assertions.assertTrue(supportedClientProtocols.contains("SSLv3"));
665 clientSocket.setEnabledProtocols(new String[] {"SSLv3"} );
666 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
667 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
668 if (isWindows()) {
669 Assertions.assertThrows(IOException.class, clientSocket::startHandshake);
670 } else {
671 Assertions.assertThrows(SSLException.class, clientSocket::startHandshake);
672 }
673 }
674 }
675
676 @Test
677 public void testSSLHandshakeProtocolMismatch2() throws Exception {
678 final URL resource1 = getResource("/test-server.p12");
679 final String storePassword = "nopassword";
680 final String keyPassword = "nopassword";
681 final SSLContext serverSslContext = SSLContextBuilder.create()
682 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
683 .build();
684 Assertions.assertNotNull(serverSslContext);
685 final URL resource2 = getResource("/test-client.p12");
686 final SSLContext clientSslContext = SSLContextBuilder.create()
687 .loadTrustMaterial(resource2, storePassword.toCharArray())
688 .build();
689 Assertions.assertNotNull(clientSslContext);
690 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
691 final Set<String> supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols()));
692 Assertions.assertTrue(supportedServerProtocols.contains("SSLv3"));
693 serverSocket.setEnabledProtocols(new String[] {"SSLv3"});
694 serverSocket.bind(new InetSocketAddress(0));
695
696 this.executorService = Executors.newSingleThreadExecutor();
697 this.executorService.submit(() -> {
698 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
699 socket.getSession();
700 }
701 return Boolean.FALSE;
702 });
703
704 final int localPort = serverSocket.getLocalPort();
705 try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
706 final Set<String> supportedClientProtocols = new LinkedHashSet<>(
707 Arrays.asList(clientSocket.getSupportedProtocols()));
708 Assertions.assertTrue(supportedClientProtocols.contains("TLSv1"));
709 clientSocket.setEnabledProtocols(new String[]{"TLSv1"});
710 clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
711 clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
712 if (isWindows()) {
713 Assertions.assertThrows(IOException.class, clientSocket::startHandshake);
714 } else {
715 Assertions.assertThrows(SSLException.class, clientSocket::startHandshake);
716 }
717 }
718 }
719
720 @Test
721 public void testJSSEEndpointIdentification() throws Exception {
722 final URL resource1 = getResource("/test-server.p12");
723 final String storePassword = "nopassword";
724 final String keyPassword = "nopassword";
725 final SSLContext serverSslContext = SSLContextBuilder.create()
726 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
727 .build();
728 Assertions.assertNotNull(serverSslContext);
729 final URL resource2 = getResource("/test-client.p12");
730 final SSLContext clientSslContext = SSLContextBuilder.create()
731 .loadTrustMaterial(resource2, storePassword.toCharArray())
732 .build();
733 Assertions.assertNotNull(clientSslContext);
734 final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
735 serverSocket.bind(new InetSocketAddress(0));
736
737 this.executorService = Executors.newSingleThreadExecutor();
738 this.executorService.submit(() -> {
739 for (;;) {
740 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
741 socket.getSession();
742 socket.shutdownOutput();
743 } catch (final IOException ex) {
744 return Boolean.FALSE;
745 }
746 }
747 });
748
749 final int localPort1 = serverSocket.getLocalPort();
750 try (final Socket clientSocket = new Socket()) {
751 clientSocket.connect(new InetSocketAddress("localhost", localPort1));
752 try (SSLSocket sslSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket(clientSocket, "localhost", -1, true)) {
753 final SSLParameters sslParameters = sslSocket.getSSLParameters();
754 sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
755 sslSocket.setSSLParameters(sslParameters);
756 sslSocket.startHandshake();
757 }
758 }
759 final int localPort2 = serverSocket.getLocalPort();
760 try (final Socket clientSocket = new Socket()) {
761 clientSocket.connect(new InetSocketAddress("localhost", localPort2));
762 try (SSLSocket sslSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket(clientSocket, "otherhost", -1, true)) {
763 final SSLParameters sslParameters = sslSocket.getSSLParameters();
764 sslParameters.setEndpointIdentificationAlgorithm(null);
765 sslSocket.setSSLParameters(sslParameters);
766 sslSocket.startHandshake();
767 }
768 }
769 final int localPort3 = serverSocket.getLocalPort();
770
771 Assertions.assertThrows(SSLException.class, () -> {
772 try (final Socket clientSocket = new Socket()) {
773 clientSocket.connect(new InetSocketAddress("localhost", localPort3));
774 try (SSLSocket sslSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket(clientSocket, "otherhost", -1, true)) {
775 final SSLParameters sslParameters = sslSocket.getSSLParameters();
776 sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
777 sslSocket.setSSLParameters(sslParameters);
778 sslSocket.startHandshake();
779 }
780 }
781 });
782 }
783
784 }