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