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 builder
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      * @param provider provider to use for creating trust stores.
150      * @return this builder
151      * @since 5.2
152      */
153     public SSLContextBuilder setTrustStoreProvider(final Provider provider) {
154         this.tsProvider = provider;
155         return this;
156     }
157 
158     /**
159      * Sets the JCA provider name to use for creating trust stores.
160      * @param name Name of the provider to use for creating trust stores, the provider must be registered with the JCA.
161      * @return this builder
162      * @since 5.2
163      */
164     public SSLContextBuilder setTrustStoreProvider(final String name) throws NoSuchProviderException {
165         this.tsProvider = requireNonNullProvider(name);
166         return this;
167     }
168 
169     /**
170      * Sets the JCA provider to use for creating key stores.
171      * @param provider provider to use for creating key stores.
172      * @return this builder
173      * @since 5.2
174      */
175     public SSLContextBuilder setKeyStoreProvider(final Provider provider) {
176         this.ksProvider = provider;
177         return this;
178     }
179 
180     /**
181      * Sets the JCA provider name to use for creating key stores.
182      * @param name Name of the provider to use for creating key stores, the provider must be registered with the JCA.
183      * @return this builder
184      * @since 5.2
185      */
186     public SSLContextBuilder setKeyStoreProvider(final String name) throws NoSuchProviderException {
187         this.ksProvider = requireNonNullProvider(name);
188         return this;
189     }
190 
191     /**
192      * Sets the key store type.
193      *
194      * @param keyStoreType
195      *            the SSLkey store type. See
196      *            the KeyStore section in the <a href=
197      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java
198      *            Cryptography Architecture Standard Algorithm Name
199      *            Documentation</a> for more information.
200      * @return this builder
201      * @see <a href=
202      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java
203      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
204      * @since 4.4.7
205      */
206     public SSLContextBuilder setKeyStoreType(final String keyStoreType) {
207         this.keyStoreType = keyStoreType;
208         return this;
209     }
210 
211     /**
212      * Sets the key manager factory algorithm name.
213      *
214      * @param keyManagerFactoryAlgorithm
215      *            the key manager factory algorithm name of the requested protocol. See
216      *            the KeyManagerFactory section in the <a href=
217      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java
218      *            Cryptography Architecture Standard Algorithm Name
219      *            Documentation</a> for more information.
220      * @return this builder
221      * @see <a href=
222      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java
223      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
224      * @since 4.4.7
225      */
226     public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) {
227         this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
228         return this;
229     }
230 
231     /**
232      * Sets the trust manager factory algorithm name.
233      *
234      * @param trustManagerFactoryAlgorithm
235      *            the trust manager algorithm name of the requested protocol. See
236      *            the TrustManagerFactory section in the <a href=
237      *            "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java
238      *            Cryptography Architecture Standard Algorithm Name
239      *            Documentation</a> for more information.
240      * @return this builder
241      * @see <a href=
242      *      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java
243      *      Cryptography Architecture Standard Algorithm Name Documentation</a>
244      * @since 4.4.7
245      */
246     public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) {
247         this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
248         return this;
249     }
250 
251     public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) {
252         this.secureRandom = secureRandom;
253         return this;
254     }
255 
256     public SSLContextBuilder loadTrustMaterial(
257             final KeyStore trustStore,
258             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
259 
260         final String alg = trustManagerFactoryAlgorithm == null ?
261                 TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm;
262 
263         final TrustManagerFactory tmFactory = tsProvider == null ?
264                 TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider);
265 
266         tmFactory.init(trustStore);
267         final TrustManager[] tms = tmFactory.getTrustManagers();
268         if (tms != null) {
269             if (trustStrategy != null) {
270                 for (int i = 0; i < tms.length; i++) {
271                     final TrustManager tm = tms[i];
272                     if (tm instanceof X509TrustManager) {
273                         tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy);
274                     }
275                 }
276             }
277             Collections.addAll(this.trustManagers, tms);
278         }
279         return this;
280     }
281 
282     /**
283      * @since 5.2
284      */
285     public SSLContextBuilder loadTrustMaterial(
286             final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
287         return loadTrustMaterial(file, null);
288     }
289 
290     /**
291      * @since 5.2
292      */
293     public SSLContextBuilder loadTrustMaterial(
294             final Path file,
295             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
296         return loadTrustMaterial(file, storePassword, null);
297     }
298 
299     /**
300      * @since 5.2
301      */
302     public SSLContextBuilder loadTrustMaterial(
303             final Path file,
304             final char[] storePassword,
305             final TrustStrategy trustStrategy,
306             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
307         Args.notNull(file, "Truststore file");
308         return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy);
309     }
310 
311     public SSLContextBuilder loadTrustMaterial(
312             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
313         return loadTrustMaterial(null, trustStrategy);
314     }
315 
316     public SSLContextBuilder loadTrustMaterial(
317             final File file,
318             final char[] storePassword,
319             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
320         Args.notNull(file, "Truststore file");
321         return loadTrustMaterial(file.toPath(), storePassword, trustStrategy);
322     }
323 
324     public SSLContextBuilder loadTrustMaterial(
325             final File file,
326             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
327         return loadTrustMaterial(file, storePassword, null);
328     }
329 
330     public SSLContextBuilder loadTrustMaterial(
331             final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
332         return loadTrustMaterial(file, null);
333     }
334 
335     public SSLContextBuilder loadTrustMaterial(
336             final URL url,
337             final char[] storePassword,
338             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
339         Args.notNull(url, "Truststore URL");
340         return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy);
341     }
342 
343     public SSLContextBuilder loadTrustMaterial(
344             final URL url,
345             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
346         return loadTrustMaterial(url, storePassword, null);
347     }
348 
349     public SSLContextBuilder loadKeyMaterial(
350             final KeyStore keyStore,
351             final char[] keyPassword,
352             final PrivateKeyStrategy aliasStrategy)
353             throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
354 
355         final String alg = keyManagerFactoryAlgorithm == null ?
356                 KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm;
357 
358         final KeyManagerFactory kmFactory = ksProvider == null ?
359                 KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider);
360 
361         kmFactory.init(keyStore, keyPassword);
362         final KeyManager[] kms = kmFactory.getKeyManagers();
363         if (kms != null) {
364             if (aliasStrategy != null) {
365                 for (int i = 0; i < kms.length; i++) {
366                     final KeyManager km = kms[i];
367                     if (km instanceof X509ExtendedKeyManager) {
368                         kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
369                     }
370                 }
371             }
372             Collections.addAll(keyManagers, kms);
373         }
374         return this;
375     }
376 
377     /**
378      * @since 5.2
379      */
380     public SSLContextBuilder loadKeyMaterial(
381             final Path file,
382             final char[] storePassword,
383             final char[] keyPassword,
384             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
385         return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions);
386     }
387 
388     /**
389      * @since 5.2
390      */
391     public SSLContextBuilder loadKeyMaterial(
392             final Path file,
393             final char[] storePassword,
394             final char[] keyPassword,
395             final PrivateKeyStrategy aliasStrategy,
396             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
397         Args.notNull(file, "Keystore file");
398         return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy);
399     }
400 
401     public SSLContextBuilder loadKeyMaterial(
402             final KeyStore keyStore,
403             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
404         return loadKeyMaterial(keyStore, keyPassword, null);
405     }
406 
407     public SSLContextBuilder loadKeyMaterial(
408             final File file,
409             final char[] storePassword,
410             final char[] keyPassword,
411             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
412         Args.notNull(file, "Keystore file");
413         return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy);
414     }
415 
416     public SSLContextBuilder loadKeyMaterial(
417             final File file,
418             final char[] storePassword,
419             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
420         return loadKeyMaterial(file, storePassword, keyPassword, null);
421     }
422 
423     public SSLContextBuilder loadKeyMaterial(
424             final URL url,
425             final char[] storePassword,
426             final char[] keyPassword,
427             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
428         Args.notNull(url, "Keystore URL");
429         return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy);
430     }
431 
432     public SSLContextBuilder loadKeyMaterial(
433             final URL url,
434             final char[] storePassword,
435             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
436         return loadKeyMaterial(url, storePassword, keyPassword, null);
437     }
438 
439     protected void initSSLContext(
440             final SSLContext sslContext,
441             final Collection<KeyManager> keyManagers,
442             final Collection<TrustManager> trustManagers,
443             final SecureRandom secureRandom) throws KeyManagementException {
444         sslContext.init(
445                 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
446                 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
447                 secureRandom);
448     }
449 
450     private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions)
451             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
452         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
453         try (final InputStream inputStream = Files.newInputStream(file, openOptions)) {
454             keyStore.load(inputStream, password);
455         }
456         return keyStore;
457     }
458 
459     private KeyStore loadKeyStore(final URL url, final char[] password)
460             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
461         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
462         try (final InputStream inputStream = url.openStream()) {
463             keyStore.load(inputStream, password);
464         }
465         return keyStore;
466     }
467 
468     public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
469         final SSLContext sslContext;
470         final String protocolStr = this.protocol != null ? this.protocol : TLS;
471         if (this.provider != null) {
472             sslContext = SSLContext.getInstance(protocolStr, this.provider);
473         } else {
474             sslContext = SSLContext.getInstance(protocolStr);
475         }
476         initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
477         return sslContext;
478     }
479 
480     static class TrustManagerDelegate implements X509TrustManager {
481 
482         private final X509TrustManager trustManager;
483         private final TrustStrategy trustStrategy;
484 
485         TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
486             this.trustManager = trustManager;
487             this.trustStrategy = trustStrategy;
488         }
489 
490         @Override
491         public void checkClientTrusted(
492                 final X509Certificate[] chain, final String authType) throws CertificateException {
493             this.trustManager.checkClientTrusted(chain, authType);
494         }
495 
496         @Override
497         public void checkServerTrusted(
498                 final X509Certificate[] chain, final String authType) throws CertificateException {
499             if (!this.trustStrategy.isTrusted(chain, authType)) {
500                 this.trustManager.checkServerTrusted(chain, authType);
501             }
502         }
503 
504         @Override
505         public X509Certificate[] getAcceptedIssuers() {
506             return this.trustManager.getAcceptedIssuers();
507         }
508 
509     }
510 
511     static class KeyManagerDelegate extends X509ExtendedKeyManager {
512 
513         private final X509ExtendedKeyManager keyManager;
514         private final PrivateKeyStrategy aliasStrategy;
515 
516         KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
517             this.keyManager = keyManager;
518             this.aliasStrategy = aliasStrategy;
519         }
520 
521         @Override
522         public String[] getClientAliases(
523                 final String keyType, final Principal[] issuers) {
524             return this.keyManager.getClientAliases(keyType, issuers);
525         }
526 
527         public Map<String, PrivateKeyDetails> getClientAliasMap(
528                 final String[] keyTypes, final Principal[] issuers) {
529             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
530             for (final String keyType: keyTypes) {
531                 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers));
532             }
533             return validAliases;
534         }
535 
536         public Map<String, PrivateKeyDetails> getServerAliasMap(
537                 final String keyType, final Principal[] issuers) {
538             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
539             putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers));
540             return validAliases;
541         }
542 
543         private void putPrivateKeyDetails(final Map<String, PrivateKeyDetails> validAliases, final String keyType,
544                 final String[] aliases) {
545             if (aliases != null) {
546                 for (final String alias: aliases) {
547                     validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
548                 }
549             }
550         }
551 
552         @Override
553         public String chooseClientAlias(
554                 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
555             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
556             return this.aliasStrategy.chooseAlias(validAliases,
557                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
558         }
559 
560         @Override
561         public String[] getServerAliases(
562                 final String keyType, final Principal[] issuers) {
563             return this.keyManager.getServerAliases(keyType, issuers);
564         }
565 
566         @Override
567         public String chooseServerAlias(
568                 final String keyType, final Principal[] issuers, final Socket socket) {
569             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
570             return this.aliasStrategy.chooseAlias(validAliases,
571                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
572         }
573 
574         @Override
575         public X509Certificate[] getCertificateChain(final String alias) {
576             return this.keyManager.getCertificateChain(alias);
577         }
578 
579         @Override
580         public PrivateKey getPrivateKey(final String alias) {
581             return this.keyManager.getPrivateKey(alias);
582         }
583 
584         @Override
585         public String chooseEngineClientAlias(
586                 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
587             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
588             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
589         }
590 
591         @Override
592         public String chooseEngineServerAlias(
593                 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
594             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
595             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
596         }
597 
598     }
599 
600     private Provider requireNonNullProvider(final String name) throws NoSuchProviderException {
601         final Provider provider = Security.getProvider(name);
602         if (provider == null) {
603             throw new NoSuchProviderException(name);
604         }
605         return provider;
606     }
607 
608     @Override
609     public String toString() {
610         return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
611                 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
612                 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
613                 + ", secureRandom=" + secureRandom + "]";
614     }
615 
616 }