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