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.PlatformDependent;
23  import io.netty.util.internal.logging.InternalLogger;
24  import io.netty.util.internal.logging.InternalLoggerFactory;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.security.InvalidAlgorithmParameterException;
29  import java.security.KeyException;
30  import java.security.KeyStore;
31  import java.security.KeyStoreException;
32  import java.security.NoSuchAlgorithmException;
33  import java.security.Provider;
34  import java.security.Security;
35  import java.security.UnrecoverableKeyException;
36  import java.security.cert.CertificateException;
37  import java.security.spec.InvalidKeySpecException;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collections;
41  import java.util.HashSet;
42  import java.util.LinkedHashSet;
43  import java.util.List;
44  import java.util.Set;
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.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 
196     /**
197      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
198      *
199      * @param sslContext the {@link SSLContext} to use.
200      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
201      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
202      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
203      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
204      */
205     @Deprecated
206     public JdkSslContext(SSLContext sslContext, boolean isClient,
207                          ClientAuth clientAuth) {
208         this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
209                 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
210     }
211 
212     /**
213      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
214      *
215      * @param sslContext the {@link SSLContext} to use.
216      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
217      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
218      * @param cipherFilter the filter to use.
219      * @param apn the {@link ApplicationProtocolConfig} to use.
220      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
221      * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter,
222      * ApplicationProtocolConfig, ClientAuth, String[], boolean)}
223      */
224     @Deprecated
225     public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
226                          CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
227                          ClientAuth clientAuth) {
228         this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false);
229     }
230 
231     /**
232      * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}.
233      *
234      * @param sslContext the {@link SSLContext} to use.
235      * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage.
236      * @param ciphers the ciphers to use or {@code null} if the standard should be used.
237      * @param cipherFilter the filter to use.
238      * @param apn the {@link ApplicationProtocolConfig} to use.
239      * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}.
240      * @param protocols the protocols to enable, or {@code null} to enable the default protocols.
241      * @param startTls {@code true} if the first write request shouldn't be encrypted
242      */
243     public JdkSslContext(SSLContext sslContext,
244                          boolean isClient,
245                          Iterable<String> ciphers,
246                          CipherSuiteFilter cipherFilter,
247                          ApplicationProtocolConfig apn,
248                          ClientAuth clientAuth,
249                          String[] protocols,
250                          boolean startTls) {
251         this(sslContext,
252                 isClient,
253                 ciphers,
254                 cipherFilter,
255                 toNegotiator(apn, !isClient),
256                 clientAuth,
257                 protocols == null ? null : protocols.clone(),
258                 startTls);
259     }
260 
261     @SuppressWarnings("deprecation")
262     JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
263                   JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
264         this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, protocols, startTls, null, null);
265     }
266 
267     @SuppressWarnings("deprecation")
268     JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
269                   JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls,
270                   String endpointIdentificationAlgorithm, ResumptionController resumptionController) {
271         super(startTls, resumptionController);
272         this.apn = checkNotNull(apn, "apn");
273         this.clientAuth = checkNotNull(clientAuth, "clientAuth");
274         this.sslContext = checkNotNull(sslContext, "sslContext");
275         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
276 
277         final List<String> defaultCiphers;
278         final Set<String> supportedCiphers;
279         if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) {
280             this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols;
281             if (isTlsV13Supported(this.protocols)) {
282                 supportedCiphers = SUPPORTED_CIPHERS;
283                 defaultCiphers = DEFAULT_CIPHERS;
284             } else {
285                 // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
286                 supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13;
287                 defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13;
288             }
289         } else {
290             // This is a different Provider then the one used by the JDK by default so we can not just assume
291             // the same protocols and ciphers are supported. For example even if Java11+ is used Conscrypt will
292             // not support TLSv1.3 and the TLSv1.3 ciphersuites.
293             SSLEngine engine = sslContext.createSSLEngine();
294             try {
295                 if (protocols == null) {
296                     this.protocols = defaultProtocols(sslContext, engine);
297                 } else {
298                     this.protocols = protocols;
299                 }
300                 supportedCiphers = supportedCiphers(engine);
301                 defaultCiphers = defaultCiphers(engine, supportedCiphers);
302                 if (!isTlsV13Supported(this.protocols)) {
303                     // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
304                     for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) {
305                         supportedCiphers.remove(cipher);
306                         defaultCiphers.remove(cipher);
307                     }
308                 }
309             } finally {
310                 ReferenceCountUtil.release(engine);
311             }
312         }
313 
314         cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
315                 ciphers, defaultCiphers, supportedCiphers);
316 
317         unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
318         this.isClient = isClient;
319     }
320 
321     /**
322      * Returns the JDK {@link SSLContext} object held by this context.
323      */
324     public final SSLContext context() {
325         return sslContext;
326     }
327 
328     @Override
329     public final boolean isClient() {
330         return isClient;
331     }
332 
333     /**
334      * Returns the JDK {@link SSLSessionContext} object held by this context.
335      */
336     @Override
337     public final SSLSessionContext sessionContext() {
338         if (isServer()) {
339             return context().getServerSessionContext();
340         } else {
341             return context().getClientSessionContext();
342         }
343     }
344 
345     @Override
346     public final List<String> cipherSuites() {
347         return unmodifiableCipherSuites;
348     }
349 
350     @Override
351     public final SSLEngine newEngine(ByteBufAllocator alloc) {
352         return configureAndWrapEngine(context().createSSLEngine(), alloc);
353     }
354 
355     @Override
356     public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
357         return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
358     }
359 
360     @SuppressWarnings("deprecation")
361     private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
362         engine.setEnabledCipherSuites(cipherSuites);
363         engine.setEnabledProtocols(protocols);
364         engine.setUseClientMode(isClient());
365         if (isServer()) {
366             switch (clientAuth) {
367                 case OPTIONAL:
368                     engine.setWantClientAuth(true);
369                     break;
370                 case REQUIRE:
371                     engine.setNeedClientAuth(true);
372                     break;
373                 case NONE:
374                     break; // exhaustive cases
375                 default:
376                     throw new Error("Unknown auth " + clientAuth);
377             }
378         }
379         configureEndpointVerification(engine);
380         JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
381         if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
382             return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
383                     .wrapSslEngine(engine, alloc, apn, isServer());
384         }
385         return factory.wrapSslEngine(engine, apn, isServer());
386     }
387 
388     private void configureEndpointVerification(SSLEngine engine) {
389         int version = PlatformDependent.javaVersion();
390         if (version >= 7) {
391             SSLParameters params = engine.getSSLParameters();
392             Java7SslParametersUtils.setEndpointIdentificationAlgorithm(params, endpointIdentificationAlgorithm);
393             engine.setSSLParameters(params);
394         }
395     }
396 
397     @Override
398     public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
399         return apn;
400     }
401 
402     /**
403      * Translate a {@link ApplicationProtocolConfig} object to a {@link JdkApplicationProtocolNegotiator} object.
404      * @param config The configuration which defines the translation
405      * @param isServer {@code true} if a server {@code false} otherwise.
406      * @return The results of the translation
407      */
408     @SuppressWarnings("deprecation")
409     static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
410         if (config == null) {
411             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
412         }
413 
414         switch(config.protocol()) {
415         case NONE:
416             return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
417         case ALPN:
418             if (isServer) {
419                 switch(config.selectorFailureBehavior()) {
420                 case FATAL_ALERT:
421                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
422                 case NO_ADVERTISE:
423                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
424                 default:
425                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
426                     .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
427                 }
428             } else {
429                 switch(config.selectedListenerFailureBehavior()) {
430                 case ACCEPT:
431                     return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
432                 case FATAL_ALERT:
433                     return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
434                 default:
435                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
436                     .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
437                 }
438             }
439         case NPN:
440             if (isServer) {
441                 switch(config.selectedListenerFailureBehavior()) {
442                 case ACCEPT:
443                     return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
444                 case FATAL_ALERT:
445                     return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
446                 default:
447                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
448                     .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
449                 }
450             } else {
451                 switch(config.selectorFailureBehavior()) {
452                 case FATAL_ALERT:
453                     return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
454                 case NO_ADVERTISE:
455                     return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
456                 default:
457                     throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
458                     .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
459                 }
460             }
461         default:
462             throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
463             .append(config.protocol()).append(" protocol").toString());
464         }
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      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
475      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
476      */
477     static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
478             KeyManagerFactory kmf, String keyStore)
479                     throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
480                     NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
481                     CertificateException, KeyException, IOException {
482         String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
483         if (algorithm == null) {
484             algorithm = "SunX509";
485         }
486         return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
487     }
488 
489     /**
490      * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
491      * @param certChainFile an X.509 certificate chain file in PEM format
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      * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
497      * @deprecated will be removed.
498      */
499     @Deprecated
500     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
501                                                               KeyManagerFactory kmf)
502             throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
503             NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
504             CertificateException, KeyException, IOException {
505         return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
506     }
507 
508     /**
509      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
510      * and a certificate chain.
511      * @param certChainFile an X.509 certificate chain file in PEM format
512      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
513      *                    Reference Guide for information about standard algorithm names.
514      * @param keyFile a PKCS#8 private key file in PEM format
515      * @param keyPassword the password of the {@code keyFile}.
516      *                    {@code null} if it's not password-protected.
517      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
518      * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
519      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
520      * and a certificate chain.
521      */
522     static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
523             String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
524             String keyStore)
525                     throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
526                     InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
527                     CertificateException, KeyException, UnrecoverableKeyException {
528         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
529                                       toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
530     }
531 
532     /**
533      * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
534      * and a certificate chain.
535      * @param certChainFile an buildKeyManagerFactory X.509 certificate chain file in PEM format
536      * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
537      *                    Reference Guide for information about standard algorithm names.
538      * @param keyFile a PKCS#8 private key file in PEM format
539      * @param keyPassword the password of the {@code keyFile}.
540      *                    {@code null} if it's not password-protected.
541      * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
542      * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
543      * and a certificate chain.
544      * @deprecated will be removed.
545      */
546     @Deprecated
547     protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
548                                                               String keyAlgorithm, File keyFile,
549                                                               String keyPassword, KeyManagerFactory kmf)
550             throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
551             InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
552             CertificateException, KeyException, UnrecoverableKeyException {
553         return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
554                 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
555     }
556 }