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     /**
257      * @param trustStrategy
258      *            custom trust strategy to use; can be {@code null} in which case
259      *            only the default trust managers will be used
260      */
261     public SSLContextBuilder loadTrustMaterial(
262             final KeyStore trustStore,
263             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
264 
265         final String alg = trustManagerFactoryAlgorithm == null ?
266                 TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm;
267 
268         final TrustManagerFactory tmFactory = tsProvider == null ?
269                 TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider);
270 
271         tmFactory.init(trustStore);
272         final TrustManager[] tms = tmFactory.getTrustManagers();
273         if (tms != null) {
274             if (trustStrategy != null) {
275                 for (int i = 0; i < tms.length; i++) {
276                     final TrustManager tm = tms[i];
277                     if (tm instanceof X509TrustManager) {
278                         tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy);
279                     }
280                 }
281             }
282             Collections.addAll(this.trustManagers, tms);
283         }
284         return this;
285     }
286 
287     /**
288      * @since 5.2
289      */
290     public SSLContextBuilder loadTrustMaterial(
291             final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
292         return loadTrustMaterial(file, null);
293     }
294 
295     /**
296      * @since 5.2
297      */
298     public SSLContextBuilder loadTrustMaterial(
299             final Path file,
300             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
301         return loadTrustMaterial(file, storePassword, null);
302     }
303 
304     /**
305      * @since 5.2
306      */
307     public SSLContextBuilder loadTrustMaterial(
308             final Path file,
309             final char[] storePassword,
310             final TrustStrategy trustStrategy,
311             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
312         Args.notNull(file, "Truststore file");
313         return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy);
314     }
315 
316     public SSLContextBuilder loadTrustMaterial(
317             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
318         return loadTrustMaterial(null, trustStrategy);
319     }
320 
321     public SSLContextBuilder loadTrustMaterial(
322             final File file,
323             final char[] storePassword,
324             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
325         Args.notNull(file, "Truststore file");
326         return loadTrustMaterial(file.toPath(), storePassword, trustStrategy);
327     }
328 
329     public SSLContextBuilder loadTrustMaterial(
330             final File file,
331             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
332         return loadTrustMaterial(file, storePassword, null);
333     }
334 
335     public SSLContextBuilder loadTrustMaterial(
336             final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
337         return loadTrustMaterial(file, null);
338     }
339 
340     public SSLContextBuilder loadTrustMaterial(
341             final URL url,
342             final char[] storePassword,
343             final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
344         Args.notNull(url, "Truststore URL");
345         return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy);
346     }
347 
348     public SSLContextBuilder loadTrustMaterial(
349             final URL url,
350             final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
351         return loadTrustMaterial(url, storePassword, null);
352     }
353 
354     public SSLContextBuilder loadKeyMaterial(
355             final KeyStore keyStore,
356             final char[] keyPassword,
357             final PrivateKeyStrategy aliasStrategy)
358             throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
359 
360         final String alg = keyManagerFactoryAlgorithm == null ?
361                 KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm;
362 
363         final KeyManagerFactory kmFactory = ksProvider == null ?
364                 KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider);
365 
366         kmFactory.init(keyStore, keyPassword);
367         final KeyManager[] kms = kmFactory.getKeyManagers();
368         if (kms != null) {
369             if (aliasStrategy != null) {
370                 for (int i = 0; i < kms.length; i++) {
371                     final KeyManager km = kms[i];
372                     if (km instanceof X509ExtendedKeyManager) {
373                         kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
374                     }
375                 }
376             }
377             Collections.addAll(keyManagers, kms);
378         }
379         return this;
380     }
381 
382     /**
383      * @since 5.2
384      */
385     public SSLContextBuilder loadKeyMaterial(
386             final Path file,
387             final char[] storePassword,
388             final char[] keyPassword,
389             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
390         return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions);
391     }
392 
393     /**
394      * @since 5.2
395      */
396     public SSLContextBuilder loadKeyMaterial(
397             final Path file,
398             final char[] storePassword,
399             final char[] keyPassword,
400             final PrivateKeyStrategy aliasStrategy,
401             final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
402         Args.notNull(file, "Keystore file");
403         return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy);
404     }
405 
406     public SSLContextBuilder loadKeyMaterial(
407             final KeyStore keyStore,
408             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
409         return loadKeyMaterial(keyStore, keyPassword, null);
410     }
411 
412     public SSLContextBuilder loadKeyMaterial(
413             final File file,
414             final char[] storePassword,
415             final char[] keyPassword,
416             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
417         Args.notNull(file, "Keystore file");
418         return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy);
419     }
420 
421     public SSLContextBuilder loadKeyMaterial(
422             final File file,
423             final char[] storePassword,
424             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
425         return loadKeyMaterial(file, storePassword, keyPassword, null);
426     }
427 
428     public SSLContextBuilder loadKeyMaterial(
429             final URL url,
430             final char[] storePassword,
431             final char[] keyPassword,
432             final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
433         Args.notNull(url, "Keystore URL");
434         return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy);
435     }
436 
437     public SSLContextBuilder loadKeyMaterial(
438             final URL url,
439             final char[] storePassword,
440             final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
441         return loadKeyMaterial(url, storePassword, keyPassword, null);
442     }
443 
444     protected void initSSLContext(
445             final SSLContext sslContext,
446             final Collection<KeyManager> keyManagers,
447             final Collection<TrustManager> trustManagers,
448             final SecureRandom secureRandom) throws KeyManagementException {
449         sslContext.init(
450                 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
451                 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
452                 secureRandom);
453     }
454 
455     private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions)
456             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
457         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
458         try (final InputStream inputStream = Files.newInputStream(file, openOptions)) {
459             keyStore.load(inputStream, password);
460         }
461         return keyStore;
462     }
463 
464     private KeyStore loadKeyStore(final URL url, final char[] password)
465             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
466         final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
467         try (final InputStream inputStream = url.openStream()) {
468             keyStore.load(inputStream, password);
469         }
470         return keyStore;
471     }
472 
473     public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
474         final SSLContext sslContext;
475         final String protocolStr = this.protocol != null ? this.protocol : TLS;
476         if (this.provider != null) {
477             sslContext = SSLContext.getInstance(protocolStr, this.provider);
478         } else {
479             sslContext = SSLContext.getInstance(protocolStr);
480         }
481         initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
482         return sslContext;
483     }
484 
485     static class TrustManagerDelegate implements X509TrustManager {
486 
487         private final X509TrustManager trustManager;
488         private final TrustStrategy trustStrategy;
489 
490         TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
491             this.trustManager = trustManager;
492             this.trustStrategy = trustStrategy;
493         }
494 
495         @Override
496         public void checkClientTrusted(
497                 final X509Certificate[] chain, final String authType) throws CertificateException {
498             this.trustManager.checkClientTrusted(chain, authType);
499         }
500 
501         @Override
502         public void checkServerTrusted(
503                 final X509Certificate[] chain, final String authType) throws CertificateException {
504             if (!this.trustStrategy.isTrusted(chain, authType)) {
505                 this.trustManager.checkServerTrusted(chain, authType);
506             }
507         }
508 
509         @Override
510         public X509Certificate[] getAcceptedIssuers() {
511             return this.trustManager.getAcceptedIssuers();
512         }
513 
514     }
515 
516     static class KeyManagerDelegate extends X509ExtendedKeyManager {
517 
518         private final X509ExtendedKeyManager keyManager;
519         private final PrivateKeyStrategy aliasStrategy;
520 
521         KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
522             this.keyManager = keyManager;
523             this.aliasStrategy = aliasStrategy;
524         }
525 
526         @Override
527         public String[] getClientAliases(
528                 final String keyType, final Principal[] issuers) {
529             return this.keyManager.getClientAliases(keyType, issuers);
530         }
531 
532         public Map<String, PrivateKeyDetails> getClientAliasMap(
533                 final String[] keyTypes, final Principal[] issuers) {
534             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
535             for (final String keyType: keyTypes) {
536                 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers));
537             }
538             return validAliases;
539         }
540 
541         public Map<String, PrivateKeyDetails> getServerAliasMap(
542                 final String keyType, final Principal[] issuers) {
543             final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
544             putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers));
545             return validAliases;
546         }
547 
548         private void putPrivateKeyDetails(final Map<String, PrivateKeyDetails> validAliases, final String keyType,
549                 final String[] aliases) {
550             if (aliases != null) {
551                 for (final String alias: aliases) {
552                     validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
553                 }
554             }
555         }
556 
557         @Override
558         public String chooseClientAlias(
559                 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
560             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
561             return this.aliasStrategy.chooseAlias(validAliases,
562                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
563         }
564 
565         @Override
566         public String[] getServerAliases(
567                 final String keyType, final Principal[] issuers) {
568             return this.keyManager.getServerAliases(keyType, issuers);
569         }
570 
571         @Override
572         public String chooseServerAlias(
573                 final String keyType, final Principal[] issuers, final Socket socket) {
574             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
575             return this.aliasStrategy.chooseAlias(validAliases,
576                     socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
577         }
578 
579         @Override
580         public X509Certificate[] getCertificateChain(final String alias) {
581             return this.keyManager.getCertificateChain(alias);
582         }
583 
584         @Override
585         public PrivateKey getPrivateKey(final String alias) {
586             return this.keyManager.getPrivateKey(alias);
587         }
588 
589         @Override
590         public String chooseEngineClientAlias(
591                 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
592             final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
593             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
594         }
595 
596         @Override
597         public String chooseEngineServerAlias(
598                 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
599             final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
600             return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
601         }
602 
603     }
604 
605     private Provider requireNonNullProvider(final String name) throws NoSuchProviderException {
606         final Provider provider = Security.getProvider(name);
607         if (provider == null) {
608             throw new NoSuchProviderException(name);
609         }
610         return provider;
611     }
612 
613     @Override
614     public String toString() {
615         return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
616                 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
617                 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
618                 + ", secureRandom=" + secureRandom + "]";
619     }
620 
621 }