View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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.Principal;
41  import java.security.Security;
42  import java.security.UnrecoverableKeyException;
43  import java.security.cert.CertificateException;
44  import java.security.cert.X509Certificate;
45  import java.util.Arrays;
46  import java.util.LinkedHashSet;
47  import java.util.Map;
48  import java.util.Set;
49  import java.util.concurrent.Callable;
50  import java.util.concurrent.ExecutorService;
51  import java.util.concurrent.Executors;
52  import java.util.concurrent.Future;
53  import java.util.concurrent.TimeUnit;
54  import java.util.concurrent.atomic.AtomicReference;
55  
56  import javax.net.ssl.KeyManagerFactory;
57  import javax.net.ssl.SSLContext;
58  import javax.net.ssl.SSLException;
59  import javax.net.ssl.SSLHandshakeException;
60  import javax.net.ssl.SSLParameters;
61  import javax.net.ssl.SSLPeerUnverifiedException;
62  import javax.net.ssl.SSLServerSocket;
63  import javax.net.ssl.SSLSession;
64  import javax.net.ssl.SSLSocket;
65  import javax.net.ssl.TrustManagerFactory;
66  
67  import org.apache.hc.core5.util.ReflectionUtils;
68  import org.apache.hc.core5.util.Timeout;
69  import org.junit.After;
70  import org.junit.Assert;
71  import org.junit.Assume;
72  import org.junit.BeforeClass;
73  import org.junit.Rule;
74  import org.junit.Test;
75  import org.junit.rules.ExpectedException;
76  
77  /**
78   * Unit tests for {@link SSLContextBuilder}.
79   */
80  public class TestSSLContextBuilder {
81  
82      private static final String PROVIDER_SUN_JSSE = "SunJSSE";
83  
84      private static boolean isWindows() {
85          return System.getProperty("os.name").contains("Windows");
86      }
87  
88      @BeforeClass
89      public static void determineJavaVersion() {
90          Assume.assumeTrue("Java version must be 8 or greater", ReflectionUtils.determineJRELevel() >= 8);
91      }
92  
93      @Rule
94      public ExpectedException thrown = ExpectedException.none();
95  
96      private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
97      private ExecutorService executorService;
98  
99      @After
100     public void cleanup() throws Exception {
101         if (this.executorService != null) {
102             this.executorService.shutdown();
103             this.executorService.awaitTermination(5, TimeUnit.SECONDS);
104         }
105     }
106 
107     private URL getResource(final String name) {
108         return getClass().getResource(name);
109     }
110 
111     @Test
112     public void testBuildAllDefaults() throws Exception {
113         final SSLContext sslContext = SSLContextBuilder.create()
114                 .setKeyStoreType(KeyStore.getDefaultType())
115                 .setKeyManagerFactoryAlgorithm(KeyManagerFactory.getDefaultAlgorithm())
116                 .setTrustManagerFactoryAlgorithm(TrustManagerFactory.getDefaultAlgorithm())
117                 .setProvider(PROVIDER_SUN_JSSE)
118                 .setProtocol("TLS")
119                 .setSecureRandom(null)
120                 .loadTrustMaterial((KeyStore) null, null)
121                 .loadKeyMaterial((KeyStore) null, null, null)
122                 .build();
123         Assert.assertNotNull(sslContext);
124         Assert.assertEquals("TLS", sslContext.getProtocol());
125         Assert.assertEquals(PROVIDER_SUN_JSSE,  sslContext.getProvider().getName());
126     }
127 
128     @Test
129     public void testBuildAllNull() throws Exception {
130         final SSLContext sslContext = SSLContextBuilder.create()
131                 .setKeyStoreType(null)
132                 .setKeyManagerFactoryAlgorithm(null)
133                 .setTrustManagerFactoryAlgorithm(null)
134                 .setProtocol(null)
135                 .setProvider((String) null)
136                 .setSecureRandom(null)
137                 .loadTrustMaterial((KeyStore) null, null)
138                 .loadKeyMaterial((KeyStore) null, null, null)
139                 .build();
140         Assert.assertNotNull(sslContext);
141         Assert.assertEquals("TLS", sslContext.getProtocol());
142         Assert.assertEquals(PROVIDER_SUN_JSSE,  sslContext.getProvider().getName());
143     }
144 
145     @Test
146     public void testBuildAllNull_deprecated() throws Exception {
147         final SSLContext sslContext = SSLContextBuilder.create()
148                 .setProtocol(null)
149                 .setSecureRandom(null)
150                 .loadTrustMaterial((KeyStore) null, null)
151                 .loadKeyMaterial((KeyStore) null, null, null)
152                 .build();
153         Assert.assertNotNull(sslContext);
154         Assert.assertEquals("TLS", sslContext.getProtocol());
155     }
156 
157     @Test
158     public void testBuildDefault() throws Exception {
159         new SSLContextBuilder().build();
160     }
161 
162     @Test(expected=NoSuchAlgorithmException.class)
163     public void testBuildNoSuchKeyManagerFactoryAlgorithm() throws Exception {
164         final URL resource1 = getResource("/test-keypasswd.p12");
165         final String storePassword = "nopassword";
166         final String keyPassword = "password";
167         SSLContextBuilder.create()
168                 .setKeyManagerFactoryAlgorithm(" BAD ")
169                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
170                 .build();
171     }
172 
173     @Test(expected= KeyStoreException.class)
174     public void testBuildNoSuchKeyStoreType() throws Exception {
175         final URL resource1 = getResource("/test-keypasswd.p12");
176         final String storePassword = "nopassword";
177         final String keyPassword = "password";
178         SSLContextBuilder.create()
179                 .setKeyStoreType(" BAD ")
180                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
181                 .build();
182     }
183 
184     @Test(expected=NoSuchAlgorithmException.class)
185     public void testBuildNoSuchTrustManagerFactoryAlgorithm() throws Exception {
186         final URL resource1 = getResource("/test-keypasswd.p12");
187         final String storePassword = "nopassword";
188         SSLContextBuilder.create()
189                 .setTrustManagerFactoryAlgorithm(" BAD ")
190                 .loadTrustMaterial(resource1, storePassword.toCharArray())
191                 .build();
192     }
193 
194     @Test
195     public void testBuildWithProvider() throws Exception {
196         final URL resource1 = getResource("/test-server.p12");
197         final String storePassword = "nopassword";
198         final String keyPassword = "nopassword";
199         final SSLContext sslContext=SSLContextBuilder.create()
200                 .setProvider(Security.getProvider(PROVIDER_SUN_JSSE))
201                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
202                 .build();
203         Assert.assertEquals(PROVIDER_SUN_JSSE,  sslContext.getProvider().getName());
204     }
205 
206     @Test
207     public void testBuildWithProviderName() throws Exception {
208         final URL resource1 = getResource("/test-server.p12");
209         final String storePassword = "nopassword";
210         final String keyPassword = "nopassword";
211         final SSLContext sslContext=SSLContextBuilder.create()
212                 .setProvider(PROVIDER_SUN_JSSE)
213                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
214                 .build();
215         Assert.assertEquals(PROVIDER_SUN_JSSE,  sslContext.getProvider().getName());
216     }
217 
218     @Test
219     public void testKeyWithAlternatePasswordInvalid() throws Exception {
220 
221         thrown.expect(UnrecoverableKeyException.class);
222 
223         final URL resource1 = getResource("/test-keypasswd.p12");
224         final String storePassword = "nopassword";
225         final String keyPassword = "!password";
226         SSLContextBuilder.create()
227                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
228                 .loadTrustMaterial(resource1, storePassword.toCharArray())
229                 .build();
230     }
231 
232     @Test
233     public void testSSLHandshakeServerTrusted() throws Exception {
234         final URL resource1 = getResource("/test.p12");
235         final String storePassword = "nopassword";
236         final String keyPassword = "nopassword";
237         final SSLContext serverSslContext = SSLContextBuilder.create()
238                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
239                 .build();
240         Assert.assertNotNull(serverSslContext);
241         final SSLContext clientSslContext = SSLContextBuilder.create()
242                 .loadTrustMaterial(resource1, storePassword.toCharArray())
243                 .build();
244         Assert.assertNotNull(clientSslContext);
245         final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
246         serverSocket.bind(new InetSocketAddress(0));
247 
248         this.executorService = Executors.newSingleThreadExecutor();
249         final Future<Boolean> future = this.executorService.submit(new Callable<Boolean>() {
250             @Override
251             public Boolean call() throws Exception {
252                 try (Socket socket = serverSocket.accept()) {
253                     final OutputStream outputStream = socket.getOutputStream();
254                     outputStream.write(new byte[]{'H', 'i'});
255                     outputStream.flush();
256                 }
257                 return Boolean.TRUE;
258             }
259         });
260 
261         final int localPort = serverSocket.getLocalPort();
262         try (final Socket clientSocket = clientSslContext.getSocketFactory().createSocket()) {
263             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
264             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
265             final InputStream inputStream = clientSocket.getInputStream();
266             Assert.assertEquals('H', inputStream.read());
267             Assert.assertEquals('i', inputStream.read());
268             Assert.assertEquals(-1, inputStream.read());
269         }
270 
271         final Boolean result = future.get(5, TimeUnit.SECONDS);
272         Assert.assertNotNull(result);
273     }
274 
275     @Test
276     public void testSSLHandshakeServerNotTrusted() throws Exception {
277         thrown.expect(IOException.class);
278 
279         final URL resource1 = getResource("/test-server.p12");
280         final String storePassword = "nopassword";
281         final String keyPassword = "nopassword";
282         final SSLContext serverSslContext = SSLContextBuilder.create()
283                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
284                 .build();
285         Assert.assertNotNull(serverSslContext);
286         final URL resource2 = getResource("/test.p12");
287         final SSLContext clientSslContext = SSLContextBuilder.create()
288                 .loadTrustMaterial(resource2, storePassword.toCharArray())
289                 .build();
290         Assert.assertNotNull(clientSslContext);
291         final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
292         serverSocket.bind(new InetSocketAddress(0));
293 
294         this.executorService = Executors.newSingleThreadExecutor();
295         this.executorService.submit(new Callable<Boolean>() {
296             @Override
297             public Boolean call() throws Exception {
298                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
299                     socket.getSession();
300                 }
301                 return Boolean.FALSE;
302             }
303         });
304         final int localPort = serverSocket.getLocalPort();
305         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
306             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
307             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
308             clientSocket.startHandshake();
309         }
310     }
311 
312     @Test
313     public void testSSLHandshakeServerCustomTrustStrategy() throws Exception {
314         final URL resource1 = getResource("/test-server.p12");
315         final String storePassword = "nopassword";
316         final String keyPassword = "nopassword";
317         final SSLContext serverSslContext = SSLContextBuilder.create()
318                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
319                 .build();
320         Assert.assertNotNull(serverSslContext);
321 
322         final AtomicReference<X509Certificate[]> certChainRef = new AtomicReference<>();
323 
324         final TrustStrategy trustStrategy = new TrustStrategy() {
325 
326             @Override
327             public boolean isTrusted(
328                     final X509Certificate[] chain, final String authType) throws CertificateException {
329                 certChainRef.set(chain);
330                 return true;
331             }
332 
333         };
334 
335         final SSLContext clientSslContext = SSLContextBuilder.create()
336                 .loadTrustMaterial(trustStrategy)
337                 .build();
338 
339         Assert.assertNotNull(clientSslContext);
340         final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket();
341         serverSocket.bind(new InetSocketAddress(0));
342 
343         this.executorService = Executors.newSingleThreadExecutor();
344         final Future<Boolean> future = this.executorService.submit(new Callable<Boolean>() {
345             @Override
346             public Boolean call() throws Exception {
347                 try (Socket socket = serverSocket.accept()) {
348                     final OutputStream outputStream = socket.getOutputStream();
349                     outputStream.write(new byte[]{'H', 'i'});
350                     outputStream.flush();
351                 }
352                 return Boolean.TRUE;
353             }
354         });
355 
356         final int localPort = serverSocket.getLocalPort();
357         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
358             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
359             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
360             final InputStream inputStream = clientSocket.getInputStream();
361             Assert.assertEquals('H', inputStream.read());
362             Assert.assertEquals('i', inputStream.read());
363             Assert.assertEquals(-1, inputStream.read());
364         }
365 
366         final Boolean result = future.get(5, TimeUnit.SECONDS);
367         Assert.assertNotNull(result);
368 
369         final X509Certificate[] certs = certChainRef.get();
370         Assert.assertNotNull(certs);
371         Assert.assertEquals(2, certs.length);
372         final X509Certificate cert1 = certs[0];
373         final Principal subjectDN1 = cert1.getSubjectDN();
374         Assert.assertNotNull(subjectDN1);
375         Assert.assertEquals("CN=Test Server, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN1.getName());
376         final X509Certificate cert2 = certs[1];
377         final Principal subjectDN2 = cert2.getSubjectDN();
378         Assert.assertNotNull(subjectDN2);
379         Assert.assertEquals("EMAILADDRESS=dev@hc.apache.org, " +
380                 "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN2.getName());
381         final Principal issuerDN = cert2.getIssuerDN();
382         Assert.assertNotNull(issuerDN);
383         Assert.assertEquals("EMAILADDRESS=dev@hc.apache.org, " +
384                 "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", issuerDN.getName());
385 
386     }
387 
388     @Test
389     public void testSSLHandshakeClientUnauthenticated() throws Exception {
390         final URL resource1 = getResource("/test-server.p12");
391         final String storePassword = "nopassword";
392         final String keyPassword = "nopassword";
393         final SSLContext serverSslContext = SSLContextBuilder.create()
394                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
395                 .build();
396         Assert.assertNotNull(serverSslContext);
397         final URL resource2 = getResource("/test-client.p12");
398         final SSLContext clientSslContext = SSLContextBuilder.create()
399                 .loadTrustMaterial(resource2, storePassword.toCharArray())
400                 .build();
401         Assert.assertNotNull(clientSslContext);
402         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
403         serverSocket.setWantClientAuth(true);
404         serverSocket.bind(new InetSocketAddress(0));
405 
406         this.executorService = Executors.newSingleThreadExecutor();
407         final Future<Principal> future = this.executorService.submit(new Callable<Principal>() {
408             @Override
409             public Principal call() throws Exception {
410                 final SSLSocket socket = (SSLSocket) serverSocket.accept();
411                 Principal clientPrincipal = null;
412                 try {
413                     final SSLSession session = socket.getSession();
414                     try {
415                         clientPrincipal = session.getPeerPrincipal();
416                     } catch (final SSLPeerUnverifiedException ignore) {
417                     }
418                     final OutputStream outputStream = socket.getOutputStream();
419                     outputStream.write(new byte [] {'H', 'i'});
420                     outputStream.flush();
421                 } finally {
422                     socket.close();
423                 }
424                 return clientPrincipal;
425             }
426         });
427 
428         final int localPort = serverSocket.getLocalPort();
429         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
430             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
431             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
432             clientSocket.startHandshake();
433             final InputStream inputStream = clientSocket.getInputStream();
434             Assert.assertEquals('H', inputStream.read());
435             Assert.assertEquals('i', inputStream.read());
436             Assert.assertEquals(-1, inputStream.read());
437         }
438 
439         final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
440         Assert.assertNull(clientPrincipal);
441     }
442 
443     @Test
444     public void testSSLHandshakeClientUnauthenticatedError() throws Exception {
445         thrown.expect(IOException.class);
446 
447         final URL resource1 = getResource("/test-server.p12");
448         final String storePassword = "nopassword";
449         final String keyPassword = "nopassword";
450         final SSLContext serverSslContext = SSLContextBuilder.create()
451                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
452                 .build();
453         Assert.assertNotNull(serverSslContext);
454         final URL resource2 = getResource("/test-client.p12");
455         final SSLContext clientSslContext = SSLContextBuilder.create()
456                 .loadTrustMaterial(resource2, storePassword.toCharArray())
457                 .build();
458         Assert.assertNotNull(clientSslContext);
459         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
460         serverSocket.setNeedClientAuth(true);
461         serverSocket.bind(new InetSocketAddress(0));
462 
463         this.executorService = Executors.newSingleThreadExecutor();
464         this.executorService.submit(new Callable<Boolean>() {
465             @Override
466             public Boolean call() throws Exception {
467                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
468                     socket.getSession();
469                 }
470                 return Boolean.FALSE;
471             }
472         });
473 
474         final int localPort = serverSocket.getLocalPort();
475         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
476             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
477             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
478             clientSocket.startHandshake();
479             final InputStream inputStream = clientSocket.getInputStream();
480             Assert.assertEquals(-1, inputStream.read());
481         }
482     }
483 
484     @Test
485     public void testSSLHandshakeClientAuthenticated() throws Exception {
486         final URL resource1 = getResource("/test-server.p12");
487         final String storePassword = "nopassword";
488         final String keyPassword = "nopassword";
489         final SSLContext serverSslContext = SSLContextBuilder.create()
490                 .loadTrustMaterial(resource1, storePassword.toCharArray())
491                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
492                 .build();
493         Assert.assertNotNull(serverSslContext);
494         final URL resource2 = getResource("/test-client.p12");
495         final SSLContext clientSslContext = SSLContextBuilder.create()
496                 .loadTrustMaterial(resource2, storePassword.toCharArray())
497                 .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray())
498                 .build();
499         Assert.assertNotNull(clientSslContext);
500         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
501         serverSocket.setNeedClientAuth(true);
502         serverSocket.bind(new InetSocketAddress(0));
503 
504         this.executorService = Executors.newSingleThreadExecutor();
505         final Future<Principal> future = this.executorService.submit(new Callable<Principal>() {
506             @Override
507             public Principal call() throws Exception {
508                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
509                     final SSLSession session = socket.getSession();
510                     final Principal clientPrincipal = session.getPeerPrincipal();
511                     final OutputStream outputStream = socket.getOutputStream();
512                     outputStream.write(new byte[]{'H', 'i'});
513                     outputStream.flush();
514                     return clientPrincipal;
515                 }
516             }
517         });
518         final int localPort = serverSocket.getLocalPort();
519         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
520             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
521             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
522             clientSocket.startHandshake();
523             final InputStream inputStream = clientSocket.getInputStream();
524             Assert.assertEquals('H', inputStream.read());
525             Assert.assertEquals('i', inputStream.read());
526             Assert.assertEquals(-1, inputStream.read());
527         }
528 
529         final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
530         Assert.assertNotNull(clientPrincipal);
531     }
532 
533     @Test
534     public void testSSLHandshakeClientAuthenticatedPrivateKeyStrategy() throws Exception {
535         final URL resource1 = getResource("/test-server.p12");
536         final String storePassword = "nopassword";
537         final String keyPassword = "nopassword";
538         final SSLContext serverSslContext = SSLContextBuilder.create()
539                 .loadTrustMaterial(resource1, storePassword.toCharArray())
540                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
541                 .build();
542         Assert.assertNotNull(serverSslContext);
543 
544         final PrivateKeyStrategy privateKeyStrategy = new PrivateKeyStrategy() {
545             @Override
546             public String chooseAlias(final Map<String, PrivateKeyDetails> aliases,
547                             final SSLParameters sslParameters) {
548                 return aliases.containsKey("client2") ? "client2" : null;
549             }
550         };
551 
552         final URL resource2 = getResource("/test-client.p12");
553         final SSLContext clientSslContext = SSLContextBuilder.create()
554                 .loadTrustMaterial(resource2, storePassword.toCharArray())
555                 .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray(), privateKeyStrategy)
556                 .build();
557         Assert.assertNotNull(clientSslContext);
558         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
559         serverSocket.setNeedClientAuth(true);
560         serverSocket.bind(new InetSocketAddress(0));
561 
562         this.executorService = Executors.newSingleThreadExecutor();
563         final Future<Principal> future = this.executorService.submit(new Callable<Principal>() {
564             @Override
565             public Principal call() throws Exception {
566                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
567                     final SSLSession session = socket.getSession();
568                     final Principal clientPrincipal = session.getPeerPrincipal();
569                     final OutputStream outputStream = socket.getOutputStream();
570                     outputStream.write(new byte[]{'H', 'i'});
571                     outputStream.flush();
572                     return clientPrincipal;
573                 }
574             }
575         });
576         final int localPort = serverSocket.getLocalPort();
577         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
578             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
579             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
580             clientSocket.startHandshake();
581             final InputStream inputStream = clientSocket.getInputStream();
582             Assert.assertEquals('H', inputStream.read());
583             Assert.assertEquals('i', inputStream.read());
584             Assert.assertEquals(-1, inputStream.read());
585         }
586 
587         final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS);
588         Assert.assertNotNull(clientPrincipal);
589         Assert.assertEquals("CN=Test Client 2,OU=HttpComponents Project,O=Apache Software Foundation", clientPrincipal.getName());
590     }
591 
592 
593     @Test
594     public void testSSLHandshakeProtocolMismatch1() throws Exception {
595         if (isWindows()) {
596             thrown.expect(IOException.class);
597         } else {
598             thrown.expect(SSLHandshakeException.class);
599         }
600 
601         final URL resource1 = getResource("/test-server.p12");
602         final String storePassword = "nopassword";
603         final String keyPassword = "nopassword";
604         final SSLContext serverSslContext = SSLContextBuilder.create()
605                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
606                 .build();
607         Assert.assertNotNull(serverSslContext);
608         final URL resource2 = getResource("/test-client.p12");
609         final SSLContext clientSslContext = SSLContextBuilder.create()
610                 .loadTrustMaterial(resource2, storePassword.toCharArray())
611                 .build();
612         Assert.assertNotNull(clientSslContext);
613         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
614         final Set<String> supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols()));
615         Assert.assertTrue(supportedServerProtocols.contains("TLSv1"));
616         serverSocket.setEnabledProtocols(new String[] {"TLSv1"});
617         serverSocket.bind(new InetSocketAddress(0));
618 
619         this.executorService = Executors.newSingleThreadExecutor();
620         this.executorService.submit(new Callable<Boolean>() {
621             @Override
622             public Boolean call() throws Exception {
623                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
624                     socket.getSession();
625                 }
626                 return Boolean.FALSE;
627             }
628         });
629 
630         final int localPort = serverSocket.getLocalPort();
631         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
632             final Set<String> supportedClientProtocols = new LinkedHashSet<>(Arrays.asList(clientSocket.getSupportedProtocols()));
633             Assert.assertTrue(supportedClientProtocols.contains("SSLv3"));
634             clientSocket.setEnabledProtocols(new String[] {"SSLv3"} );
635             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
636             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
637             clientSocket.startHandshake();
638         }
639     }
640 
641     @Test
642     public void testSSLHandshakeProtocolMismatch2() throws Exception {
643         if (isWindows()) {
644             thrown.expect(IOException.class);
645         } else {
646             thrown.expect(SSLException.class);
647         }
648 
649         final URL resource1 = getResource("/test-server.p12");
650         final String storePassword = "nopassword";
651         final String keyPassword = "nopassword";
652         final SSLContext serverSslContext = SSLContextBuilder.create()
653                 .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray())
654                 .build();
655         Assert.assertNotNull(serverSslContext);
656         final URL resource2 = getResource("/test-client.p12");
657         final SSLContext clientSslContext = SSLContextBuilder.create()
658                 .loadTrustMaterial(resource2, storePassword.toCharArray())
659                 .build();
660         Assert.assertNotNull(clientSslContext);
661         final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket();
662         final Set<String> supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols()));
663         Assert.assertTrue(supportedServerProtocols.contains("SSLv3"));
664         serverSocket.setEnabledProtocols(new String[] {"SSLv3"});
665         serverSocket.bind(new InetSocketAddress(0));
666 
667         this.executorService = Executors.newSingleThreadExecutor();
668         this.executorService.submit(new Callable<Boolean>() {
669             @Override
670             public Boolean call() throws Exception {
671                 try (SSLSocket socket = (SSLSocket) serverSocket.accept()) {
672                     socket.getSession();
673                 }
674                 return Boolean.FALSE;
675             }
676         });
677 
678         final int localPort = serverSocket.getLocalPort();
679         try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) {
680             final Set<String> supportedClientProtocols = new LinkedHashSet<>(
681                     Arrays.asList(clientSocket.getSupportedProtocols()));
682             Assert.assertTrue(supportedClientProtocols.contains("TLSv1"));
683             clientSocket.setEnabledProtocols(new String[] { "TLSv1" });
684             clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound());
685             clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound());
686             clientSocket.startHandshake();
687             final InputStream inputStream = clientSocket.getInputStream();
688             Assert.assertEquals(-1, inputStream.read());
689         }
690     }
691 
692 }