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