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.File;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.net.Socket;
34  import java.net.URL;
35  import java.nio.file.Files;
36  import java.nio.file.OpenOption;
37  import java.nio.file.Path;
38  import java.security.KeyManagementException;
39  import java.security.KeyStore;
40  import java.security.KeyStoreException;
41  import java.security.NoSuchAlgorithmException;
42  import java.security.NoSuchProviderException;
43  import java.security.Principal;
44  import java.security.PrivateKey;
45  import java.security.Provider;
46  import java.security.SecureRandom;
47  import java.security.Security;
48  import java.security.UnrecoverableKeyException;
49  import java.security.cert.CertificateException;
50  import java.security.cert.X509Certificate;
51  import java.util.Collection;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.LinkedHashSet;
55  import java.util.Map;
56  import java.util.Set;
57  
58  import javax.net.ssl.KeyManager;
59  import javax.net.ssl.KeyManagerFactory;
60  import javax.net.ssl.SSLContext;
61  import javax.net.ssl.SSLEngine;
62  import javax.net.ssl.SSLSocket;
63  import javax.net.ssl.TrustManager;
64  import javax.net.ssl.TrustManagerFactory;
65  import javax.net.ssl.X509ExtendedKeyManager;
66  import javax.net.ssl.X509TrustManager;
67  
68  import org.apache.hc.core5.util.Args;
69  
70  /**
71   * Builder for {@link javax.net.ssl.SSLContext} instances.
72   * <p>
73   * Please note: the default Oracle JSSE implementation of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}
74   * accepts multiple key and trust managers, however only only first matching type is ever used.
75   * See for example:
76   * <a href="http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html#init%28javax.net.ssl.KeyManager[],%20javax.net.ssl.TrustManager[],%20java.security.SecureRandom%29">
77   * SSLContext.html#init
78   * </a>
79   *
80   * @since 4.4
81   */
82  public class SSLContextBuilder {
83  
84      static final String TLS   = "TLS";
85  
86      private String protocol;
87      private final Set<KeyManager> keyManagers;
88      private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
89      private String keyStoreType = KeyStore.getDefaultType();
90      private final Set<TrustManager> trustManagers;
91      private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
92      private SecureRandom secureRandom;
93      private Provider provider;
94      private Provider tsProvider;
95      private Provider ksProvider;
96  
97      /**
98       * An empty immutable {@code KeyManager} array.
99       */
100     private static final KeyManager[] EMPTY_KEY_MANAGER_ARRAY = {};
101 
102     /**
103      * An empty immutable {@code TrustManager} array.
104      */
105     private static final TrustManager[] EMPTY_TRUST_MANAGER_ARRAY = {};
106 
107 
108 
109     public static SSLContextBuilder create() {
110         return new SSLContextBuilder();
111     }
112 
113     public SSLContextBuilder() {
114         this.keyManagers = new LinkedHashSet<>();
115         this.trustManagers = new LinkedHashSet<>();
116     }
117 
118     /**
119      * Sets the SSLContext algorithm name.
120      *
121      * @param protocol
122      *            the SSLContext algorithm name of the requested protocol. See
123      *            the SSLContext section in the <a href=
124      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java
125      *            Cryptography Architecture Standard Algorithm Name
126      *            Documentation</a> for more information.
127      * @return this instance.
128      * @see <a href=
129      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java
130      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
131      */
132     public SSLContextBuilder setProtocol(final String protocol) {
133         this.protocol = protocol;
134         return this;
135     }
136 
137     public SSLContextBuilder setProvider(final Provider provider) {
138         this.provider = provider;
139         return this;
140     }
141 
142     public SSLContextBuilder setProvider(final String name) {
143         this.provider = Security.getProvider(name);
144         return this;
145     }
146 
147     /**
148      * Sets the JCA provider to use for creating trust stores.
149      *
150      * @param provider provider to use for creating trust stores.
151      * @return this instance.
152      * @since 5.2
153      */
154     public SSLContextBuilder setTrustStoreProvider(final Provider provider) {
155         this.tsProvider = provider;
156         return this;
157     }
158 
159     /**
160      * Sets the JCA provider name to use for creating trust stores.
161      *
162      * @param name Name of the provider to use for creating trust stores, the provider must be registered with the JCA.
163      * @return this instance.
164      * @throws NoSuchProviderException if no provider with the specified name is installed or if name is null.
165      * @since 5.2
166      */
167     public SSLContextBuilder setTrustStoreProvider(final String name) throws NoSuchProviderException {
168         this.tsProvider = requireNonNullProvider(name);
169         return this;
170     }
171 
172     /**
173      * Sets the JCA provider to use for creating key stores.
174      *
175      * @param provider provider to use for creating key stores.
176      * @return this instance.
177      * @since 5.2
178      */
179     public SSLContextBuilder setKeyStoreProvider(final Provider provider) {
180         this.ksProvider = provider;
181         return this;
182     }
183 
184     /**
185      * Sets the JCA provider name to use for creating key stores.
186      *
187      * @param name Name of the provider to use for creating key stores, the provider must be registered with the JCA.
188      * @return this instance.
189      * @throws NoSuchProviderException if no provider with the specified name is installed or if name is null.
190      * @since 5.2
191      */
192     public SSLContextBuilder setKeyStoreProvider(final String name) throws NoSuchProviderException {
193         this.ksProvider = requireNonNullProvider(name);
194         return this;
195     }
196 
197     /**
198      * Sets the key store type.
199      *
200      * @param keyStoreType
201      *            the SSLkey store type. See
202      *            the KeyStore section in the <a href=
203      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java
204      *            Cryptography Architecture Standard Algorithm Name
205      *            Documentation</a> for more information.
206      * @return this instance.
207      * @see <a href=
208      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java
209      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
210      * @since 4.4.7
211      */
212     public SSLContextBuilder setKeyStoreType(final String keyStoreType) {
213         this.keyStoreType = keyStoreType;
214         return this;
215     }
216 
217     /**
218      * Sets the key manager factory algorithm name.
219      *
220      * @param keyManagerFactoryAlgorithm
221      *            the key manager factory algorithm name of the requested protocol. See
222      *            the KeyManagerFactory section in the <a href=
223      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java
224      *            Cryptography Architecture Standard Algorithm Name
225      *            Documentation</a> for more information.
226      * @return this instance.
227      * @see <a href=
228      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java
229      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
230      * @since 4.4.7
231      */
232     public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) {
233         this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
234         return this;
235     }
236 
237     /**
238      * Sets the trust manager factory algorithm name.
239      *
240      * @param trustManagerFactoryAlgorithm
241      *            the trust manager algorithm name of the requested protocol. See
242      *            the TrustManagerFactory section in the <a href=
243      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java
244      *            Cryptography Architecture Standard Algorithm Name
245      *            Documentation</a> for more information.
246      * @return this instance.
247      * @see <a href=
248      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java
249      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
250      * @since 4.4.7
251      */
252     public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) {
253         this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
254         return this;
255     }
256 
257     public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) {
258         this.secureRandom = secureRandom;
259         return this;
260     }
261 
262     /**
263      * @param trustStrategy
264      *            custom trust strategy to use; can be {@code null} in which case
265      *            only the default trust managers will be used
266      * @return this instance.
267      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
268      * @throws KeyStoreException if a TrustManagerFactory operation fails.
269      */
270     public SSLContextBuilder loadTrustMaterial(
271             final KeyStore trustStore,
272             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
273 
274         final String alg = trustManagerFactoryAlgorithm == null ?
275                 TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm;
276 
277         final TrustManagerFactory tmFactory = tsProvider == null ?
278                 TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider);
279 
280         tmFactory.init(trustStore);
281         final TrustManager[] tms = tmFactory.getTrustManagers();
282         if (tms != null) {
283             if (trustStrategy != null) {
284                 for (int i = 0; i < tms.length; i++) {
285                     final TrustManager tm = tms[i];
286                     if (tm instanceof X509TrustManager) {
287                         tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy);
288                     }
289                 }
290             }
291             Collections.addAll(this.trustManagers, tms);
292         }
293         return this;
294     }
295 
296     /**
297      * @return this instance.
298      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
299      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type.
300      * @throws CertificateException if any of the certificates in the keystore could not be loaded.
301      * @throws IOException if an I/O exception occurs.
302      * @since 5.2
303      */
304     public SSLContextBuilder loadTrustMaterial(
305             final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
306         return loadTrustMaterial(file, null);
307     }
308 
309     /**
310      * @return this instance.
311      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
312      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type.
313      * @throws CertificateException if any of the certificates in the keystore could not be loaded.
314      * @throws IOException if an I/O exception occurs.
315      * @since 5.2
316      */
317     public SSLContextBuilder loadTrustMaterial(
318             final Path file,
319             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
320         return loadTrustMaterial(file, storePassword, null);
321     }
322 
323     /**
324      * @return this instance.
325      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
326      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type.
327      * @throws CertificateException if any of the certificates in the keystore could not be loaded.
328      * @throws IOException if an I/O exception occurs.
329      * @since 5.2
330      */
331     public SSLContextBuilder loadTrustMaterial(
332             final Path file,
333             final char[] storePassword,
334             final TrustStrategy trustStrategy,
335             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
336         Args.notNull(file, "Truststore file");
337         return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy);
338     }
339 
340     public SSLContextBuilder loadTrustMaterial(
341             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
342         return loadTrustMaterial(null, trustStrategy);
343     }
344 
345     public SSLContextBuilder loadTrustMaterial(
346             final File file,
347             final char[] storePassword,
348             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
349         Args.notNull(file, "Truststore file");
350         return loadTrustMaterial(file.toPath(), storePassword, trustStrategy);
351     }
352 
353     public SSLContextBuilder loadTrustMaterial(
354             final File file,
355             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
356         return loadTrustMaterial(file, storePassword, null);
357     }
358 
359     public SSLContextBuilder loadTrustMaterial(
360             final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
361         return loadTrustMaterial(file, null);
362     }
363 
364     public SSLContextBuilder loadTrustMaterial(
365             final URL url,
366             final char[] storePassword,
367             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
368         Args.notNull(url, "Truststore URL");
369         return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy);
370     }
371 
372     public SSLContextBuilder loadTrustMaterial(
373             final URL url,
374             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
375         return loadTrustMaterial(url, storePassword, null);
376     }
377 
378     public SSLContextBuilder loadKeyMaterial(
379             final KeyStore keyStore,
380             final char[] keyPassword,
381             final PrivateKeyStrategy aliasStrategy)
382             throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
383 
384         final String alg = keyManagerFactoryAlgorithm == null ?
385                 KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm;
386 
387         final KeyManagerFactory kmFactory = ksProvider == null ?
388                 KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider);
389 
390         kmFactory.init(keyStore, keyPassword);
391         final KeyManager[] kms = kmFactory.getKeyManagers();
392         if (kms != null) {
393             if (aliasStrategy != null) {
394                 for (int i = 0; i < kms.length; i++) {
395                     final KeyManager km = kms[i];
396                     if (km instanceof X509ExtendedKeyManager) {
397                         kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
398                     }
399                 }
400             }
401             Collections.addAll(keyManagers, kms);
402         }
403         return this;
404     }
405 
406     /**
407      * @return this instance.
408      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
409      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type.
410      * @throws CertificateException if any of the certificates in the keystore could not be loaded.
411      * @throws IOException if an I/O exception occurs.
412      * @throws UnrecoverableKeyException if the key cannot be recovered (for example, the given password is wrong).
413      * @since 5.2
414      */
415     public SSLContextBuilder loadKeyMaterial(
416             final Path file,
417             final char[] storePassword,
418             final char[] keyPassword,
419             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
420         return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions);
421     }
422 
423     /**
424      * @return this instance.
425      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
426      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type.
427      * @throws CertificateException if any of the certificates in the keystore could not be loaded.
428      * @throws IOException if an I/O exception occurs.
429      * @throws UnrecoverableKeyException if the key cannot be recovered (for example, the given password is wrong).
430      * @since 5.2
431      */
432     public SSLContextBuilder loadKeyMaterial(
433             final Path file,
434             final char[] storePassword,
435             final char[] keyPassword,
436             final PrivateKeyStrategy aliasStrategy,
437             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
438         Args.notNull(file, "Keystore file");
439         return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy);
440     }
441 
442     public SSLContextBuilder loadKeyMaterial(
443             final KeyStore keyStore,
444             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
445         return loadKeyMaterial(keyStore, keyPassword, null);
446     }
447 
448     public SSLContextBuilder loadKeyMaterial(
449             final File file,
450             final char[] storePassword,
451             final char[] keyPassword,
452             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
453         Args.notNull(file, "Keystore file");
454         return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy);
455     }
456 
457     public SSLContextBuilder loadKeyMaterial(
458             final File file,
459             final char[] storePassword,
460             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
461         return loadKeyMaterial(file, storePassword, keyPassword, null);
462     }
463 
464     public SSLContextBuilder loadKeyMaterial(
465             final URL url,
466             final char[] storePassword,
467             final char[] keyPassword,
468             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
469         Args.notNull(url, "Keystore URL");
470         return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy);
471     }
472 
473     public SSLContextBuilder loadKeyMaterial(
474             final URL url,
475             final char[] storePassword,
476             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
477         return loadKeyMaterial(url, storePassword, keyPassword, null);
478     }
479 
480     /**
481      * @throws KeyManagementException if this SSLContext operation fails.
482      */
483     protected void initSSLContext(
484             final SSLContext sslContext,
485             final Collection<KeyManager> keyManagers,
486             final Collection<TrustManager> trustManagers,
487             final SecureRandom secureRandom) throws KeyManagementException {
488         sslContext.init(
489                 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
490                 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
491                 secureRandom);
492     }
493 
494     /**
495      * @throws KeyStoreException        if no Provider supports a KeyStoreSpi implementation for the specified type.
496      * @throws IOException              if an I/O error occurs.
497      * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found.
498      * @throws CertificateException     if any of the certificates in the keystore could not be loaded
499      */
500     private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions)
501             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
502         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
503         try (final InputStream inputStream = Files.newInputStream(file, openOptions)) {
504             keyStore.load(inputStream, password);
505         }
506         return keyStore;
507     }
508 
509     private KeyStore loadKeyStore(final URL url, final char[] password)
510             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
511         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
512         try (final InputStream inputStream = url.openStream()) {
513             keyStore.load(inputStream, password);
514         }
515         return keyStore;
516     }
517 
518     /**
519      * Builds a new SSLContext.
520      *
521      * @return a new SSLContext.
522      * @throws NoSuchAlgorithmException if no Provider supports a KeyManagerFactorySpi implementation for the specified algorithm.
523      * @throws KeyManagementException if this SSLContext operation fails.
524      */
525     public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
526         final SSLContext sslContext;
527         final String protocolStr = this.protocol != null ? this.protocol : TLS;
528         if (this.provider != null) {
529             sslContext = SSLContext.getInstance(protocolStr, this.provider);
530         } else {
531             sslContext = SSLContext.getInstance(protocolStr);
532         }
533         initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
534         return sslContext;
535     }
536 
537     static class TrustManagerDelegate implements X509TrustManager {
538 
539         private final X509TrustManager trustManager;
540         private final TrustStrategy trustStrategy;
541 
542         TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
543             this.trustManager = trustManager;
544             this.trustStrategy = trustStrategy;
545         }
546 
547         @Override
548         public void checkClientTrusted(
549                 final X509Certificate[] chain, final String authType) throws CertificateException {
550             this.trustManager.checkClientTrusted(chain, authType);
551         }
552 
553         @Override
554         public void checkServerTrusted(
555                 final X509Certificate[] chain, final String authType) throws CertificateException {
556             if (!this.trustStrategy.isTrusted(chain, authType)) {
557                 this.trustManager.checkServerTrusted(chain, authType);
558             }
559         }
560 
561         @Override
562         public X509Certificate[] getAcceptedIssuers() {
563             return this.trustManager.getAcceptedIssuers();
564         }
565 
566     }
567 
568     static class KeyManagerDelegate extends X509ExtendedKeyManager {
569 
570         private final X509ExtendedKeyManager keyManager;
571         private final PrivateKeyStrategy aliasStrategy;
572 
573         KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
574             this.keyManager = keyManager;
575             this.aliasStrategy = aliasStrategy;
576         }
577 
578         @Override
579         public String[] getClientAliases(
580                 final String keyType, final Principal[] issuers) {
581             return this.keyManager.getClientAliases(keyType, issuers);
582         }
583 
584         public Map<String, PrivateKeyDetails> getClientAliasMap(
585                 final String[] keyTypes, final Principal[] issuers) {
586             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
587             for (final String keyType: keyTypes) {
588                 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers));
589             }
590             return validAliases;
591         }
592 
593         public Map<String, PrivateKeyDetails> getServerAliasMap(
594                 final String keyType, final Principal[] issuers) {
595             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
596             putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers));
597             return validAliases;
598         }
599 
600         private void putPrivateKeyDetails(final Map<String, PrivateKeyDetails> validAliases, final String keyType,
601                 final String[] aliases) {
602             if (aliases != null) {
603                 for (final String alias: aliases) {
604                     validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
605                 }
606             }
607         }
608 
609         @Override
610         public String chooseClientAlias(
611                 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
612             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
613             return this.aliasStrategy.chooseAlias(validAliases,
614                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
615         }
616 
617         @Override
618         public String[] getServerAliases(
619                 final String keyType, final Principal[] issuers) {
620             return this.keyManager.getServerAliases(keyType, issuers);
621         }
622 
623         @Override
624         public String chooseServerAlias(
625                 final String keyType, final Principal[] issuers, final Socket socket) {
626             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
627             return this.aliasStrategy.chooseAlias(validAliases,
628                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
629         }
630 
631         @Override
632         public X509Certificate[] getCertificateChain(final String alias) {
633             return this.keyManager.getCertificateChain(alias);
634         }
635 
636         @Override
637         public PrivateKey getPrivateKey(final String alias) {
638             return this.keyManager.getPrivateKey(alias);
639         }
640 
641         @Override
642         public String chooseEngineClientAlias(
643                 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
644             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
645             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
646         }
647 
648         @Override
649         public String chooseEngineServerAlias(
650                 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
651             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
652             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
653         }
654 
655     }
656 
657     /**
658      * @throws NoSuchProviderException if no provider with the specified name is installed or if name is null.
659      */
660     private Provider requireNonNullProvider(final String name) throws NoSuchProviderException {
661         final Provider provider = Security.getProvider(name);
662         if (provider == null) {
663             throw new NoSuchProviderException(name);
664         }
665         return provider;
666     }
667 
668     @Override
669     public String toString() {
670         return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
671                 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
672                 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
673                 + ", secureRandom=" + secureRandom + "]";
674     }
675 
676 }