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  
45  import javax.crypto.NoSuchPaddingException;
46  import javax.net.ssl.KeyManagerFactory;
47  import javax.net.ssl.SSLContext;
48  import javax.net.ssl.SSLEngine;
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 
194     /**
195      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
196      *
197      * @param sslContext the {@link SSLContext} to use.
198      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
199      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
200      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
201      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
202      */
203     @Deprecated
204     public JdkSslContext(SSLContext sslContext, boolean isClient,
205                          ClientAuth clientAuth) {
206         this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
207                 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
208     }
209 
210     /**
211      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
212      *
213      * @param sslContext the {@link SSLContext} to use.
214      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
215      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
216      * @param cipherFilter the filter to use.
217      * @param apn the {@link ApplicationProtocolConfig} to use.
218      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
219      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
220      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
221      */
222     @Deprecated
223     public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
224                          CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
225                          ClientAuth clientAuth) {
226         this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false);
227     }
228 
229     /**
230      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
231      *
232      * @param sslContext the {@link SSLContext} to use.
233      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
234      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
235      * @param cipherFilter the filter to use.
236      * @param apn the {@link ApplicationProtocolConfig} to use.
237      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
238      * @param protocols the protocols to enable, or {@code null} to enable the default protocols.
239      * @param startTls {@code true} if the first write request shouldn't be encrypted
240      */
241     public JdkSslContext(SSLContext sslContext,
242                          boolean isClient,
243                          Iterable<String> ciphers,
244                          CipherSuiteFilter cipherFilter,
245                          ApplicationProtocolConfig apn,
246                          ClientAuth clientAuth,
247                          String[] protocols,
248                          boolean startTls) {
249         this(sslContext,
250                 isClient,
251                 ciphers,
252                 cipherFilter,
253                 toNegotiator(apn, !isClient),
254                 clientAuth,
255                 protocols == null ? null : protocols.clone(),
256                 startTls);
257     }
258 
259     @SuppressWarnings("deprecation")
260     JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
261                   JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
262         super(startTls);
263         this.apn = checkNotNull(apn, "apn");
264         this.clientAuth = checkNotNull(clientAuth, "clientAuth");
265         this.sslContext = checkNotNull(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 {
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             } finally {
300                 ReferenceCountUtil.release(engine);
301             }
302         }
303 
304         cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
305                 ciphers, defaultCiphers, supportedCiphers);
306 
307         unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
308         this.isClient = isClient;
309     }
310 
311     /**
312      * Returns the JDK {@link SSLContext} object held by this context.
313      */
314     public final SSLContext context() {
315         return sslContext;
316     }
317 
318     @Override
319     public final boolean isClient() {
320         return isClient;
321     }
322 
323     /**
324      * Returns the JDK {@link SSLSessionContext} object held by this context.
325      */
326     @Override
327     public final SSLSessionContext sessionContext() {
328         if (isServer()) {
329             return context().getServerSessionContext();
330         } else {
331             return context().getClientSessionContext();
332         }
333     }
334 
335     @Override
336     public final List<String> cipherSuites() {
337         return unmodifiableCipherSuites;
338     }
339 
340     @Override
341     public final SSLEngine newEngine(ByteBufAllocator alloc) {
342         return configureAndWrapEngine(context().createSSLEngine(), alloc);
343     }
344 
345     @Override
346     public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
347         return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
348     }
349 
350     @SuppressWarnings("deprecation")
351     private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
352         engine.setEnabledCipherSuites(cipherSuites);
353         engine.setEnabledProtocols(protocols);
354         engine.setUseClientMode(isClient());
355         if (isServer()) {
356             switch (clientAuth) {
357                 case OPTIONAL:
358                     engine.setWantClientAuth(true);
359                     break;
360                 case REQUIRE:
361                     engine.setNeedClientAuth(true);
362                     break;
363                 case NONE:
364                     break; // exhaustive cases
365                 default:
366                     throw new Error("Unknown auth " + clientAuth);
367             }
368         }
369         JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
370         if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
371             return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
372                     .wrapSslEngine(engine, alloc, apn, isServer());
373         }
374         return factory.wrapSslEngine(engine, apn, isServer());
375     }
376 
377     @Override
378     public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
379         return apn;
380     }
381 
382     /**
383      * Translate a {@link ApplicationProtocolConfig} object to a {@link JdkApplicationProtocolNegotiator} object.
384      * @param config The configuration which defines the translation
385      * @param isServer {@code true} if a server {@code false} otherwise.
386      * @return The results of the translation
387      */
388     @SuppressWarnings("deprecation")
389     static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
390         if (config == null) {
391             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
392         }
393 
394         switch(config.protocol()) {
395         case NONE:
396             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
397         case ALPN:
398             if (isServer) {
399                 switch(config.selectorFailureBehavior()) {
400                 case FATAL_ALERT:
401                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
402                 case NO_ADVERTISE:
403                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
404                 default:
405                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
406                     .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
407                 }
408             } else {
409                 switch(config.selectedListenerFailureBehavior()) {
410                 case ACCEPT:
411                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
412                 case FATAL_ALERT:
413                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
414                 default:
415                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
416                     .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
417                 }
418             }
419         case NPN:
420             if (isServer) {
421                 switch(config.selectedListenerFailureBehavior()) {
422                 case ACCEPT:
423                     return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
424                 case FATAL_ALERT:
425                     return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
426                 default:
427                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
428                     .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
429                 }
430             } else {
431                 switch(config.selectorFailureBehavior()) {
432                 case FATAL_ALERT:
433                     return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
434                 case NO_ADVERTISE:
435                     return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
436                 default:
437                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
438                     .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
439                 }
440             }
441         default:
442             throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
443             .append(config.protocol()).append(" protocol").toString());
444         }
445     }
446 
447     /**
448      * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
449      * @param certChainFile an X.509 certificate chain file in PEM format
450      * @param keyFile a PKCS#8 private key file in PEM format
451      * @param keyPassword the password of the {@code keyFile}.
452      *                    {@code null} if it's not password-protected.
453      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
454      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
455      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
456      */
457     static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
458             KeyManagerFactory kmf, String keyStore)
459                     throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
460                     NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
461                     CertificateException, KeyException, IOException {
462         String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
463         if (algorithm == null) {
464             algorithm = "SunX509";
465         }
466         return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
467     }
468 
469     /**
470      * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
471      * @param certChainFile an X.509 certificate chain file in PEM format
472      * @param keyFile a PKCS#8 private key file in PEM format
473      * @param keyPassword the password of the {@code keyFile}.
474      *                    {@code null} if it's not password-protected.
475      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
476      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
477      * @deprecated will be removed.
478      */
479     @Deprecated
480     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
481                                                               KeyManagerFactory kmf)
482             throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
483             NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
484             CertificateException, KeyException, IOException {
485         return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
486     }
487 
488     /**
489      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
490      * and a certificate chain.
491      * @param certChainFile an X.509 certificate chain file in PEM format
492      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
493      *                    Reference Guide for information about standard algorithm names.
494      * @param keyFile a PKCS#8 private key file in PEM format
495      * @param keyPassword the password of the {@code keyFile}.
496      *                    {@code null} if it's not password-protected.
497      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
498      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
499      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
500      * and a certificate chain.
501      */
502     static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
503             String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
504             String keyStore)
505                     throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
506                     InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
507                     CertificateException, KeyException, UnrecoverableKeyException {
508         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
509                                       toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
510     }
511 
512     /**
513      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
514      * and a certificate chain.
515      * @param certChainFile an buildKeyManagerFactory X.509 certificate chain file in PEM format
516      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
517      *                    Reference Guide for information about standard algorithm names.
518      * @param keyFile a PKCS#8 private key file in PEM format
519      * @param keyPassword the password of the {@code keyFile}.
520      *                    {@code null} if it's not password-protected.
521      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
522      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
523      * and a certificate chain.
524      * @deprecated will be removed.
525      */
526     @Deprecated
527     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
528                                                               String keyAlgorithm, File keyFile,
529                                                               String keyPassword, KeyManagerFactory kmf)
530             throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
531             InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
532             CertificateException, KeyException, UnrecoverableKeyException {
533         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
534                 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
535     }
536 }