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