View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.handler.ssl;
18  
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.util.ReferenceCountUtil;
21  import io.netty.util.internal.EmptyArrays;
22  import io.netty.util.internal.logging.InternalLogger;
23  import io.netty.util.internal.logging.InternalLoggerFactory;
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.security.InvalidAlgorithmParameterException;
28  import java.security.KeyException;
29  import java.security.KeyStore;
30  import java.security.KeyStoreException;
31  import java.security.NoSuchAlgorithmException;
32  import java.security.Provider;
33  import java.security.Security;
34  import java.security.UnrecoverableKeyException;
35  import java.security.cert.CertificateException;
36  import java.security.spec.InvalidKeySpecException;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collections;
40  import java.util.HashSet;
41  import java.util.LinkedHashSet;
42  import java.util.List;
43  import java.util.Set;
44  import javax.crypto.NoSuchPaddingException;
45  import javax.net.ssl.KeyManagerFactory;
46  import javax.net.ssl.SSLContext;
47  import javax.net.ssl.SSLEngine;
48  import javax.net.ssl.SSLParameters;
49  import javax.net.ssl.SSLSessionContext;
50  
51  import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
52  import static io.netty.handler.ssl.SslUtils.addIfSupported;
53  import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
54  import static io.netty.util.internal.ObjectUtil.checkNotNull;
55  
56  /**
57   * An {@link SslContext} which uses JDK's SSL/TLS implementation.
58   */
59  public class JdkSslContext extends SslContext {
60  
61      private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class);
62  
63      static final String PROTOCOL = "TLS";
64      private static final String[] DEFAULT_PROTOCOLS;
65      private static final List<String> DEFAULT_CIPHERS;
66      private static final List<String> DEFAULT_CIPHERS_NON_TLSV13;
67      private static final Set<String> SUPPORTED_CIPHERS;
68      private static final Set<String> SUPPORTED_CIPHERS_NON_TLSV13;
69      private static final Provider DEFAULT_PROVIDER;
70  
71      static {
72          Defaults defaults = new Defaults();
73          defaults.init();
74  
75          DEFAULT_PROVIDER = defaults.defaultProvider;
76          DEFAULT_PROTOCOLS = defaults.defaultProtocols;
77          SUPPORTED_CIPHERS = defaults.supportedCiphers;
78          DEFAULT_CIPHERS = defaults.defaultCiphers;
79          DEFAULT_CIPHERS_NON_TLSV13 = defaults.defaultCiphersNonTLSv13;
80          SUPPORTED_CIPHERS_NON_TLSV13 = defaults.supportedCiphersNonTLSv13;
81  
82          if (logger.isDebugEnabled()) {
83              logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
84              logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
85          }
86      }
87  
88      private static final class Defaults {
89          String[] defaultProtocols;
90          List<String> defaultCiphers;
91          List<String> defaultCiphersNonTLSv13;
92          Set<String> supportedCiphers;
93          Set<String> supportedCiphersNonTLSv13;
94          Provider defaultProvider;
95  
96          void init() {
97              SSLContext context;
98              try {
99                  context = SSLContext.getInstance(PROTOCOL);
100                 context.init(null, null, null);
101             } catch (Exception e) {
102                 throw new Error("failed to initialize the default SSL context", e);
103             }
104 
105             defaultProvider = context.getProvider();
106 
107             SSLEngine engine = context.createSSLEngine();
108             defaultProtocols = defaultProtocols(context, engine);
109 
110             supportedCiphers = Collections.unmodifiableSet(supportedCiphers(engine));
111             defaultCiphers = Collections.unmodifiableList(defaultCiphers(engine, supportedCiphers));
112 
113             List<String> ciphersNonTLSv13 = new ArrayList<String>(defaultCiphers);
114             ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
115             defaultCiphersNonTLSv13 = Collections.unmodifiableList(ciphersNonTLSv13);
116 
117             Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(supportedCiphers);
118             suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
119             supportedCiphersNonTLSv13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13);
120         }
121     }
122 
123     private static String[] defaultProtocols(SSLContext context, SSLEngine engine) {
124         // Choose the sensible default list of protocols that respects JDK flags, eg. jdk.tls.client.protocols
125         final String[] supportedProtocols = context.getDefaultSSLParameters().getProtocols();
126         Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length);
127         Collections.addAll(supportedProtocolsSet, supportedProtocols);
128         List<String> protocols = new ArrayList<String>();
129         addIfSupported(
130                 supportedProtocolsSet, protocols,
131                 SslProtocols.TLS_v1_3, SslProtocols.TLS_v1_2,
132                 SslProtocols.TLS_v1_1, SslProtocols.TLS_v1);
133 
134         if (!protocols.isEmpty()) {
135             return protocols.toArray(EmptyArrays.EMPTY_STRINGS);
136         }
137         return engine.getEnabledProtocols();
138     }
139 
140     private static Set<String> supportedCiphers(SSLEngine engine) {
141         // Choose the sensible default list of cipher suites.
142         final String[] supportedCiphers = engine.getSupportedCipherSuites();
143         Set<String> supportedCiphersSet = new LinkedHashSet<String>(supportedCiphers.length);
144         for (int i = 0; i < supportedCiphers.length; ++i) {
145             String supportedCipher = supportedCiphers[i];
146             supportedCiphersSet.add(supportedCipher);
147             // IBM's J9 JVM utilizes a custom naming scheme for ciphers and only returns ciphers with the "SSL_"
148             // prefix instead of the "TLS_" prefix (as defined in the JSSE cipher suite names [1]). According to IBM's
149             // documentation [2] the "SSL_" prefix is "interchangeable" with the "TLS_" prefix.
150             // See the IBM forum discussion [3] and issue on IBM's JVM [4] for more details.
151             //[1] https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites
152             //[2] https://www.ibm.com/support/knowledgecenter/en/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/
153             // security-component/jsse2Docs/ciphersuites.html
154             //[3] https://www.ibm.com/developerworks/community/forums/html/topic?id=9b5a56a9-fa46-4031-b33b-df91e28d77c2
155             //[4] https://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=71770
156             if (supportedCipher.startsWith("SSL_")) {
157                 final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length());
158                 try {
159                     engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName});
160                     supportedCiphersSet.add(tlsPrefixedCipherName);
161                 } catch (IllegalArgumentException ignored) {
162                     // The cipher is not supported ... move on to the next cipher.
163                 }
164             }
165         }
166         return supportedCiphersSet;
167     }
168 
169     private static List<String> defaultCiphers(SSLEngine engine, Set<String> supportedCiphers) {
170         List<String> ciphers = new ArrayList<String>();
171         addIfSupported(supportedCiphers, ciphers, DEFAULT_CIPHER_SUITES);
172         useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
173         return ciphers;
174     }
175 
176     private static boolean isTlsV13Supported(String[] protocols) {
177         for (String protocol: protocols) {
178             if (SslProtocols.TLS_v1_3.equals(protocol)) {
179                 return true;
180             }
181         }
182         return false;
183     }
184 
185     private final String[] protocols;
186     private final String[] cipherSuites;
187     private final List<String> unmodifiableCipherSuites;
188     @SuppressWarnings("deprecation")
189     private final JdkApplicationProtocolNegotiator apn;
190     private final ClientAuth clientAuth;
191     private final SSLContext sslContext;
192     private final boolean isClient;
193     private final String endpointIdentificationAlgorithm;
194 
195     /**
196      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
197      *
198      * @param sslContext the {@link SSLContext} to use.
199      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
200      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
201      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
202      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
203      */
204     @Deprecated
205     public JdkSslContext(SSLContext sslContext, boolean isClient,
206                          ClientAuth clientAuth) {
207         this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
208                 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
209     }
210 
211     /**
212      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
213      *
214      * @param sslContext the {@link SSLContext} to use.
215      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
216      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
217      * @param cipherFilter the filter to use.
218      * @param apn the {@link ApplicationProtocolConfig} to use.
219      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
220      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
221      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
222      */
223     @Deprecated
224     public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
225                          CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
226                          ClientAuth clientAuth) {
227         this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false);
228     }
229 
230     /**
231      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
232      *
233      * @param sslContext the {@link SSLContext} to use.
234      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
235      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
236      * @param cipherFilter the filter to use.
237      * @param apn the {@link ApplicationProtocolConfig} to use.
238      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
239      * @param protocols the protocols to enable, or {@code null} to enable the default protocols.
240      * @param startTls {@code true} if the first write request shouldn't be encrypted
241      */
242     public JdkSslContext(SSLContext sslContext,
243                          boolean isClient,
244                          Iterable<String> ciphers,
245                          CipherSuiteFilter cipherFilter,
246                          ApplicationProtocolConfig apn,
247                          ClientAuth clientAuth,
248                          String[] protocols,
249                          boolean startTls) {
250         this(sslContext,
251                 isClient,
252                 ciphers,
253                 cipherFilter,
254                 toNegotiator(apn, !isClient),
255                 clientAuth,
256                 protocols == null ? null : protocols.clone(),
257                 startTls);
258     }
259 
260     @SuppressWarnings("deprecation")
261     JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
262                   JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
263         this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, protocols, startTls, null, null);
264     }
265 
266     @SuppressWarnings("deprecation")
267     JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
268                   JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls,
269                   String endpointIdentificationAlgorithm, ResumptionController resumptionController) {
270         super(startTls, resumptionController);
271         this.apn = checkNotNull(apn, "apn");
272         this.clientAuth = checkNotNull(clientAuth, "clientAuth");
273         this.sslContext = checkNotNull(sslContext, "sslContext");
274         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
275 
276         final List<String> defaultCiphers;
277         final Set<String> supportedCiphers;
278         if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) {
279             this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols;
280             if (isTlsV13Supported(this.protocols)) {
281                 supportedCiphers = SUPPORTED_CIPHERS;
282                 defaultCiphers = DEFAULT_CIPHERS;
283             } else {
284                 // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
285                 supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13;
286                 defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13;
287             }
288         } else {
289             // This is a different Provider then the one used by the JDK by default so we can not just assume
290             // the same protocols and ciphers are supported. For example even if Java11+ is used Conscrypt will
291             // not support TLSv1.3 and the TLSv1.3 ciphersuites.
292             SSLEngine engine = sslContext.createSSLEngine();
293             try {
294                 if (protocols == null) {
295                     this.protocols = defaultProtocols(sslContext, engine);
296                 } else {
297                     this.protocols = protocols;
298                 }
299                 supportedCiphers = supportedCiphers(engine);
300                 defaultCiphers = defaultCiphers(engine, supportedCiphers);
301                 if (!isTlsV13Supported(this.protocols)) {
302                     // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
303                     for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) {
304                         supportedCiphers.remove(cipher);
305                         defaultCiphers.remove(cipher);
306                     }
307                 }
308             } finally {
309                 ReferenceCountUtil.release(engine);
310             }
311         }
312 
313         cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
314                 ciphers, defaultCiphers, supportedCiphers);
315 
316         unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
317         this.isClient = isClient;
318     }
319 
320     /**
321      * Returns the JDK {@link SSLContext} object held by this context.
322      */
323     public final SSLContext context() {
324         return sslContext;
325     }
326 
327     @Override
328     public final boolean isClient() {
329         return isClient;
330     }
331 
332     /**
333      * Returns the JDK {@link SSLSessionContext} object held by this context.
334      */
335     @Override
336     public final SSLSessionContext sessionContext() {
337         if (isServer()) {
338             return context().getServerSessionContext();
339         } else {
340             return context().getClientSessionContext();
341         }
342     }
343 
344     @Override
345     public final List<String> cipherSuites() {
346         return unmodifiableCipherSuites;
347     }
348 
349     @Override
350     public final SSLEngine newEngine(ByteBufAllocator alloc) {
351         return configureAndWrapEngine(context().createSSLEngine(), alloc);
352     }
353 
354     @Override
355     public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
356         return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
357     }
358 
359     @SuppressWarnings("deprecation")
360     private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
361         engine.setEnabledCipherSuites(cipherSuites);
362         engine.setEnabledProtocols(protocols);
363         engine.setUseClientMode(isClient());
364         if (isServer()) {
365             switch (clientAuth) {
366                 case OPTIONAL:
367                     engine.setWantClientAuth(true);
368                     break;
369                 case REQUIRE:
370                     engine.setNeedClientAuth(true);
371                     break;
372                 case NONE:
373                     break; // exhaustive cases
374                 default:
375                     throw new Error("Unknown auth " + clientAuth);
376             }
377         }
378         configureEndpointVerification(engine);
379         JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
380         if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
381             return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
382                     .wrapSslEngine(engine, alloc, apn, isServer());
383         }
384         return factory.wrapSslEngine(engine, apn, isServer());
385     }
386 
387     private void configureEndpointVerification(SSLEngine engine) {
388         SSLParameters params = engine.getSSLParameters();
389         params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
390         engine.setSSLParameters(params);
391     }
392 
393     @Override
394     public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
395         return apn;
396     }
397 
398     /**
399      * Translate a {@link ApplicationProtocolConfig} object to a {@link JdkApplicationProtocolNegotiator} object.
400      * @param config The configuration which defines the translation
401      * @param isServer {@code true} if a server {@code false} otherwise.
402      * @return The results of the translation
403      */
404     @SuppressWarnings("deprecation")
405     static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
406         if (config == null) {
407             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
408         }
409 
410         switch(config.protocol()) {
411         case NONE:
412             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
413         case ALPN:
414             if (isServer) {
415                 switch(config.selectorFailureBehavior()) {
416                 case FATAL_ALERT:
417                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
418                 case NO_ADVERTISE:
419                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
420                 default:
421                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
422                     .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
423                 }
424             } else {
425                 switch(config.selectedListenerFailureBehavior()) {
426                 case ACCEPT:
427                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
428                 case FATAL_ALERT:
429                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
430                 default:
431                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
432                     .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
433                 }
434             }
435         default:
436             throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
437             .append(config.protocol()).append(" protocol").toString());
438         }
439     }
440 
441     /**
442      * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
443      * @param certChainFile an X.509 certificate chain file in PEM format
444      * @param keyFile a PKCS#8 private key file in PEM format
445      * @param keyPassword the password of the {@code keyFile}.
446      *                    {@code null} if it's not password-protected.
447      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
448      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
449      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
450      */
451     static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
452             KeyManagerFactory kmf, String keyStore)
453                     throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
454                     NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
455                     CertificateException, KeyException, IOException {
456         String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
457         if (algorithm == null) {
458             algorithm = "SunX509";
459         }
460         return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
461     }
462 
463     /**
464      * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
465      * @param certChainFile an X.509 certificate chain file in PEM format
466      * @param keyFile a PKCS#8 private key file in PEM format
467      * @param keyPassword the password of the {@code keyFile}.
468      *                    {@code null} if it's not password-protected.
469      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
470      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
471      * @deprecated will be removed.
472      */
473     @Deprecated
474     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
475                                                               KeyManagerFactory kmf)
476             throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
477             NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
478             CertificateException, KeyException, IOException {
479         return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
480     }
481 
482     /**
483      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
484      * and a certificate chain.
485      * @param certChainFile an X.509 certificate chain file in PEM format
486      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
487      *                    Reference Guide for information about standard algorithm names.
488      * @param keyFile a PKCS#8 private key file in PEM format
489      * @param keyPassword the password of the {@code keyFile}.
490      *                    {@code null} if it's not password-protected.
491      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
492      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
493      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
494      * and a certificate chain.
495      */
496     static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
497             String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
498             String keyStore)
499                     throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
500                     InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
501                     CertificateException, KeyException, UnrecoverableKeyException {
502         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
503                                       toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
504     }
505 
506     /**
507      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
508      * and a certificate chain.
509      * @param certChainFile an buildKeyManagerFactory X.509 certificate chain file in PEM format
510      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
511      *                    Reference Guide for information about standard algorithm names.
512      * @param keyFile a PKCS#8 private key file in PEM format
513      * @param keyPassword the password of the {@code keyFile}.
514      *                    {@code null} if it's not password-protected.
515      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
516      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
517      * and a certificate chain.
518      * @deprecated will be removed.
519      */
520     @Deprecated
521     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
522                                                               String keyAlgorithm, File keyFile,
523                                                               String keyPassword, KeyManagerFactory kmf)
524             throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
525             InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
526             CertificateException, KeyException, UnrecoverableKeyException {
527         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
528                 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
529     }
530 }