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.BufferInputStream;
19  import io.netty5.buffer.api.Buffer;
20  import io.netty5.buffer.api.BufferAllocator;
21  import io.netty5.channel.ChannelInitializer;
22  import io.netty5.channel.ChannelPipeline;
23  import io.netty5.handler.ssl.ApplicationProtocolConfig.Protocol;
24  import io.netty5.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
25  import io.netty5.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
26  import io.netty5.util.AttributeMap;
27  import io.netty5.util.DefaultAttributeMap;
28  import io.netty5.util.internal.EmptyArrays;
29  
30  import javax.crypto.Cipher;
31  import javax.crypto.EncryptedPrivateKeyInfo;
32  import javax.crypto.NoSuchPaddingException;
33  import javax.crypto.SecretKey;
34  import javax.crypto.SecretKeyFactory;
35  import javax.crypto.spec.PBEKeySpec;
36  import javax.net.ssl.KeyManager;
37  import javax.net.ssl.KeyManagerFactory;
38  import javax.net.ssl.SSLContext;
39  import javax.net.ssl.SSLEngine;
40  import javax.net.ssl.SSLException;
41  import javax.net.ssl.SSLSessionContext;
42  import javax.net.ssl.TrustManager;
43  import javax.net.ssl.TrustManagerFactory;
44  import java.io.File;
45  import java.io.IOException;
46  import java.io.InputStream;
47  import java.io.UncheckedIOException;
48  import java.security.InvalidAlgorithmParameterException;
49  import java.security.InvalidKeyException;
50  import java.security.KeyException;
51  import java.security.KeyFactory;
52  import java.security.KeyStore;
53  import java.security.KeyStoreException;
54  import java.security.NoSuchAlgorithmException;
55  import java.security.PrivateKey;
56  import java.security.Provider;
57  import java.security.UnrecoverableKeyException;
58  import java.security.cert.CertificateException;
59  import java.security.cert.CertificateFactory;
60  import java.security.cert.X509Certificate;
61  import java.security.spec.InvalidKeySpecException;
62  import java.security.spec.PKCS8EncodedKeySpec;
63  import java.util.List;
64  import java.util.Map;
65  import java.util.concurrent.Executor;
66  
67  /**
68   * A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
69   * Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
70   *
71   * <h3>Making your server support SSL/TLS</h3>
72   * <pre>
73   * // In your {@link ChannelInitializer}:
74   * {@link ChannelPipeline} p = channel.pipeline();
75   * {@link SslContext} sslCtx = {@link SslContextBuilder#forServer(File, File) SslContextBuilder.forServer(...)}.build();
76   * p.addLast("ssl", {@link #newHandler(BufferAllocator) sslCtx.newHandler(channel.alloc())});
77   * ...
78   * </pre>
79   *
80   * <h3>Making your client support SSL/TLS</h3>
81   * <pre>
82   * // In your {@link ChannelInitializer}:
83   * {@link ChannelPipeline} p = channel.pipeline();
84   * {@link SslContext} sslCtx = {@link SslContextBuilder#forClient() SslContextBuilder.forClient()}.build();
85   * p.addLast("ssl", {@link #newHandler(BufferAllocator, String, int) sslCtx.newHandler(channel.alloc(), host, port)});
86   * ...
87   * </pre>
88   */
89  public abstract class SslContext {
90      static final String ALIAS = "key";
91  
92      static final CertificateFactory X509_CERT_FACTORY;
93      static {
94          try {
95              X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
96          } catch (CertificateException e) {
97              throw new IllegalStateException("unable to instance X.509 CertificateFactory", e);
98          }
99      }
100 
101     private final boolean startTls;
102     private final AttributeMap attributes = new DefaultAttributeMap();
103 
104     /**
105      * Returns the default server-side implementation provider currently in use.
106      *
107      * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
108      */
109     public static SslProvider defaultServerProvider() {
110         return defaultProvider();
111     }
112 
113     /**
114      * Returns the default client-side implementation provider currently in use.
115      *
116      * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
117      */
118     public static SslProvider defaultClientProvider() {
119         return defaultProvider();
120     }
121 
122     private static SslProvider defaultProvider() {
123         if (OpenSsl.isAvailable()) {
124             return SslProvider.OPENSSL;
125         } else {
126             return SslProvider.JDK;
127         }
128     }
129 
130     /**
131      * Creates a new server-side {@link SslContext}.
132      *
133      * @param certChainFile an X.509 certificate chain file in PEM format
134      * @param keyFile a PKCS#8 private key file in PEM format
135      * @return a new server-side {@link SslContext}
136      * @deprecated Replaced by {@link SslContextBuilder}
137      */
138     @Deprecated
139     public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
140         return newServerContext(certChainFile, keyFile, null);
141     }
142 
143     /**
144      * Creates a new server-side {@link SslContext}.
145      *
146      * @param certChainFile an X.509 certificate chain file in PEM format
147      * @param keyFile a PKCS#8 private key file in PEM format
148      * @param keyPassword the password of the {@code keyFile}.
149      *                    {@code null} if it's not password-protected.
150      * @return a new server-side {@link SslContext}
151      * @deprecated Replaced by {@link SslContextBuilder}
152      */
153     @Deprecated
154     public static SslContext newServerContext(
155             File certChainFile, File keyFile, String keyPassword) throws SSLException {
156         return newServerContext(null, certChainFile, keyFile, keyPassword);
157     }
158 
159     /**
160      * Creates a new server-side {@link SslContext}.
161      *
162      * @param certChainFile an X.509 certificate chain file in PEM format
163      * @param keyFile a PKCS#8 private key file in PEM format
164      * @param keyPassword the password of the {@code keyFile}.
165      *                    {@code null} if it's not password-protected.
166      * @param ciphers the cipher suites to enable, in the order of preference.
167      *                {@code null} to use the default cipher suites.
168      * @param nextProtocols the application layer protocols to accept, in the order of preference.
169      *                      {@code null} to disable TLS NPN/ALPN extension.
170      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
171      *                         {@code 0} to use the default value.
172      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
173      *                       {@code 0} to use the default value.
174      * @return a new server-side {@link SslContext}
175      * @deprecated Replaced by {@link SslContextBuilder}
176      */
177     @Deprecated
178     public static SslContext newServerContext(
179             File certChainFile, File keyFile, String keyPassword,
180             Iterable<String> ciphers, Iterable<String> nextProtocols,
181             long sessionCacheSize, long sessionTimeout) throws SSLException {
182 
183         return newServerContext(
184                 null, certChainFile, keyFile, keyPassword,
185                 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
186     }
187 
188     /**
189      * Creates a new server-side {@link SslContext}.
190      *
191      * @param certChainFile an X.509 certificate chain file in PEM format
192      * @param keyFile a PKCS#8 private key file in PEM format
193      * @param keyPassword the password of the {@code keyFile}.
194      *                    {@code null} if it's not password-protected.
195      * @param ciphers the cipher suites to enable, in the order of preference.
196      *                {@code null} to use the default cipher suites.
197      * @param cipherFilter a filter to apply over the supplied list of ciphers
198      * @param apn Provides a means to configure parameters related to application protocol negotiation.
199      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
200      *                         {@code 0} to use the default value.
201      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
202      *                       {@code 0} to use the default value.
203      * @return a new server-side {@link SslContext}
204      * @deprecated Replaced by {@link SslContextBuilder}
205      */
206     @Deprecated
207     public static SslContext newServerContext(
208             File certChainFile, File keyFile, String keyPassword,
209             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
210             long sessionCacheSize, long sessionTimeout) throws SSLException {
211         return newServerContext(
212                 null, certChainFile, keyFile, keyPassword,
213                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
214     }
215 
216     /**
217      * Creates a new server-side {@link SslContext}.
218      *
219      * @param provider the {@link SslContext} implementation to use.
220      *                 {@code null} to use the current default one.
221      * @param certChainFile an X.509 certificate chain file in PEM format
222      * @param keyFile a PKCS#8 private key file in PEM format
223      * @return a new server-side {@link SslContext}
224      * @deprecated Replaced by {@link SslContextBuilder}
225      */
226     @Deprecated
227     public static SslContext newServerContext(
228             SslProvider provider, File certChainFile, File keyFile) throws SSLException {
229         return newServerContext(provider, certChainFile, keyFile, null);
230     }
231 
232     /**
233      * Creates a new server-side {@link SslContext}.
234      *
235      * @param provider the {@link SslContext} implementation to use.
236      *                 {@code null} to use the current default one.
237      * @param certChainFile an X.509 certificate chain file in PEM format
238      * @param keyFile a PKCS#8 private key file in PEM format
239      * @param keyPassword the password of the {@code keyFile}.
240      *                    {@code null} if it's not password-protected.
241      * @return a new server-side {@link SslContext}
242      * @deprecated Replaced by {@link SslContextBuilder}
243      */
244     @Deprecated
245     public static SslContext newServerContext(
246             SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
247         return newServerContext(provider, certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
248                                 null, 0, 0);
249     }
250 
251     /**
252      * Creates a new server-side {@link SslContext}.
253      *
254      * @param provider the {@link SslContext} implementation to use.
255      *                 {@code null} to use the current default one.
256      * @param certChainFile an X.509 certificate chain file in PEM format
257      * @param keyFile a PKCS#8 private key file in PEM format
258      * @param keyPassword the password of the {@code keyFile}.
259      *                    {@code null} if it's not password-protected.
260      * @param ciphers the cipher suites to enable, in the order of preference.
261      *                {@code null} to use the default cipher suites.
262      * @param nextProtocols the application layer protocols to accept, in the order of preference.
263      *                      {@code null} to disable TLS NPN/ALPN extension.
264      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
265      *                         {@code 0} to use the default value.
266      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
267      *                       {@code 0} to use the default value.
268      * @return a new server-side {@link SslContext}
269      * @deprecated Replaced by {@link SslContextBuilder}
270      */
271     @Deprecated
272     public static SslContext newServerContext(
273             SslProvider provider,
274             File certChainFile, File keyFile, String keyPassword,
275             Iterable<String> ciphers, Iterable<String> nextProtocols,
276             long sessionCacheSize, long sessionTimeout) throws SSLException {
277         return newServerContext(provider, certChainFile, keyFile, keyPassword,
278                                 ciphers, IdentityCipherSuiteFilter.INSTANCE,
279                                 toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
280     }
281 
282     /**
283      * Creates a new server-side {@link SslContext}.
284      *
285      * @param provider the {@link SslContext} implementation to use.
286      *                 {@code null} to use the current default one.
287      * @param certChainFile an X.509 certificate chain file in PEM format
288      * @param keyFile a PKCS#8 private key file in PEM format
289      * @param keyPassword the password of the {@code keyFile}.
290      *                    {@code null} if it's not password-protected.
291      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
292      *                            that verifies the certificates sent from servers.
293      *                            {@code null} to use the default.
294      * @param ciphers the cipher suites to enable, in the order of preference.
295      *                {@code null} to use the default cipher suites.
296      * @param nextProtocols the application layer protocols to accept, in the order of preference.
297      *                      {@code null} to disable TLS NPN/ALPN extension.
298      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
299      *                         {@code 0} to use the default value.
300      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
301      *                       {@code 0} to use the default value.
302      * @return a new server-side {@link SslContext}
303      * @deprecated Replaced by {@link SslContextBuilder}
304      */
305     @Deprecated
306     public static SslContext newServerContext(
307             SslProvider provider,
308             File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
309             Iterable<String> ciphers, Iterable<String> nextProtocols,
310             long sessionCacheSize, long sessionTimeout) throws SSLException {
311 
312         return newServerContext(
313                 provider, null, trustManagerFactory, certChainFile, keyFile, keyPassword,
314                 null, ciphers, IdentityCipherSuiteFilter.INSTANCE,
315                 toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
316     }
317 
318     /**
319      * Creates a new server-side {@link SslContext}.
320      *
321      * @param provider the {@link SslContext} implementation to use.
322      *                 {@code null} to use the current default one.
323      * @param certChainFile an X.509 certificate chain file in PEM format
324      * @param keyFile a PKCS#8 private key file in PEM format
325      * @param keyPassword the password of the {@code keyFile}.
326      *                    {@code null} if it's not password-protected.
327      * @param ciphers the cipher suites to enable, in the order of preference.
328      *                {@code null} to use the default cipher suites.
329      * @param cipherFilter a filter to apply over the supplied list of ciphers
330      *                Only required if {@code provider} is {@link SslProvider#JDK}
331      * @param apn Provides a means to configure parameters related to application protocol negotiation.
332      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
333      *                         {@code 0} to use the default value.
334      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
335      *                       {@code 0} to use the default value.
336      * @return a new server-side {@link SslContext}
337      * @deprecated Replaced by {@link SslContextBuilder}
338      */
339     @Deprecated
340     public static SslContext newServerContext(SslProvider provider,
341             File certChainFile, File keyFile, String keyPassword,
342             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
343             long sessionCacheSize, long sessionTimeout) throws SSLException {
344         return newServerContext(provider, null, null, certChainFile, keyFile, keyPassword, null,
345                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
346     }
347 
348     /**
349      * Creates a new server-side {@link SslContext}.
350      * @param provider the {@link SslContext} implementation to use.
351      *                 {@code null} to use the current default one.
352      * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
353      *                      This provides the certificate collection used for mutual authentication.
354      *                      {@code null} to use the system default
355      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
356      *                            that verifies the certificates sent from clients.
357      *                            {@code null} to use the default or the results of parsing
358      *                            {@code trustCertCollectionFile}.
359      *                            This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
360      * @param keyCertChainFile an X.509 certificate chain file in PEM format
361      * @param keyFile a PKCS#8 private key file in PEM format
362      * @param keyPassword the password of the {@code keyFile}.
363      *                    {@code null} if it's not password-protected.
364      * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
365      *                          that is used to encrypt data being sent to clients.
366      *                          {@code null} to use the default or the results of parsing
367      *                          {@code keyCertChainFile} and {@code keyFile}.
368      *                          This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
369      * @param ciphers the cipher suites to enable, in the order of preference.
370      *                {@code null} to use the default cipher suites.
371      * @param cipherFilter a filter to apply over the supplied list of ciphers
372      *                Only required if {@code provider} is {@link SslProvider#JDK}
373      * @param apn Provides a means to configure parameters related to application protocol negotiation.
374      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
375      *                         {@code 0} to use the default value.
376      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
377      *                       {@code 0} to use the default value.
378      * @return a new server-side {@link SslContext}
379      * @deprecated Replaced by {@link SslContextBuilder}
380      */
381     @Deprecated
382     public static SslContext newServerContext(
383             SslProvider provider,
384             File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
385             File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
386             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
387             long sessionCacheSize, long sessionTimeout) throws SSLException {
388         return newServerContext(provider, trustCertCollectionFile, trustManagerFactory, keyCertChainFile,
389                 keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
390                 sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
391     }
392 
393     /**
394      * Creates a new server-side {@link SslContext}.
395      * @param provider the {@link SslContext} implementation to use.
396      *                 {@code null} to use the current default one.
397      * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
398      *                      This provides the certificate collection used for mutual authentication.
399      *                      {@code null} to use the system default
400      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
401      *                            that verifies the certificates sent from clients.
402      *                            {@code null} to use the default or the results of parsing
403      *                            {@code trustCertCollectionFile}.
404      *                            This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
405      * @param keyCertChainFile an X.509 certificate chain file in PEM format
406      * @param keyFile a PKCS#8 private key file in PEM format
407      * @param keyPassword the password of the {@code keyFile}.
408      *                    {@code null} if it's not password-protected.
409      * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
410      *                          that is used to encrypt data being sent to clients.
411      *                          {@code null} to use the default or the results of parsing
412      *                          {@code keyCertChainFile} and {@code keyFile}.
413      *                          This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
414      * @param ciphers the cipher suites to enable, in the order of preference.
415      *                {@code null} to use the default cipher suites.
416      * @param cipherFilter a filter to apply over the supplied list of ciphers
417      *                Only required if {@code provider} is {@link SslProvider#JDK}
418      * @param apn Provides a means to configure parameters related to application protocol negotiation.
419      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
420      *                         {@code 0} to use the default value.
421      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
422      *                       {@code 0} to use the default value.
423      * @param keyStore the keystore type that should  be used
424      * @return a new server-side {@link SslContext}
425      */
426     static SslContext newServerContext(
427             SslProvider provider,
428             File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
429             File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
430             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
431             long sessionCacheSize, long sessionTimeout, String keyStore) throws SSLException {
432         try {
433             return newServerContextInternal(provider, null, toX509Certificates(trustCertCollectionFile),
434                                             trustManagerFactory, toX509Certificates(keyCertChainFile),
435                                             toPrivateKey(keyFile, keyPassword),
436                                             keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
437                                             sessionCacheSize, sessionTimeout, ClientAuth.NONE, null,
438                                             false, false, keyStore);
439         } catch (Exception e) {
440             if (e instanceof SSLException) {
441                 throw (SSLException) e;
442             }
443             throw new SSLException("failed to initialize the server-side SSL context", e);
444         }
445     }
446 
447     static SslContext newServerContextInternal(
448             SslProvider provider,
449             Provider sslContextProvider,
450             X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
451             X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
452             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
453             long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
454             boolean enableOcsp, String keyStoreType, Map.Entry<SslContextOption<?>, Object>... ctxOptions)
455             throws SSLException {
456 
457         if (provider == null) {
458             provider = defaultServerProvider();
459         }
460 
461         switch (provider) {
462         case JDK:
463             if (enableOcsp) {
464                 throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
465             }
466             try {
467                 return new JdkSslServerContext(sslContextProvider,
468                         trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
469                         keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
470                         clientAuth, protocols, startTls, keyStoreType);
471             } catch (SSLException e) {
472                 throw e;
473             } catch (Exception e) {
474                 throw new SSLException("Failed to build SslContext", e);
475             }
476         case OPENSSL:
477             verifyNullSslContextProvider(provider, sslContextProvider);
478             return new OpenSslServerContext(
479                     trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
480                     keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
481                     clientAuth, protocols, startTls, enableOcsp, keyStoreType, ctxOptions);
482         case OPENSSL_REFCNT:
483             verifyNullSslContextProvider(provider, sslContextProvider);
484             return new ReferenceCountedOpenSslServerContext(
485                     trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
486                     keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
487                     clientAuth, protocols, startTls, enableOcsp, keyStoreType, ctxOptions);
488         default:
489             throw new Error(provider.toString());
490         }
491     }
492 
493     private static void verifyNullSslContextProvider(SslProvider provider, Provider sslContextProvider) {
494         if (sslContextProvider != null) {
495             throw new IllegalArgumentException("Java Security Provider unsupported for SslProvider: " + provider);
496         }
497     }
498 
499     /**
500      * Creates a new client-side {@link SslContext}.
501      *
502      * @return a new client-side {@link SslContext}
503      * @deprecated Replaced by {@link SslContextBuilder}
504      */
505     @Deprecated
506     public static SslContext newClientContext() throws SSLException {
507         return newClientContext(null, null, null);
508     }
509 
510     /**
511      * Creates a new client-side {@link SslContext}.
512      *
513      * @param certChainFile an X.509 certificate chain file in PEM format
514      *
515      * @return a new client-side {@link SslContext}
516      * @deprecated Replaced by {@link SslContextBuilder}
517      */
518     @Deprecated
519     public static SslContext newClientContext(File certChainFile) throws SSLException {
520         return newClientContext(null, certChainFile);
521     }
522 
523     /**
524      * Creates a new client-side {@link SslContext}.
525      *
526      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
527      *                            that verifies the certificates sent from servers.
528      *                            {@code null} to use the default.
529      *
530      * @return a new client-side {@link SslContext}
531      * @deprecated Replaced by {@link SslContextBuilder}
532      */
533     @Deprecated
534     public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
535         return newClientContext(null, null, trustManagerFactory);
536     }
537 
538     /**
539      * Creates a new client-side {@link SslContext}.
540      *
541      * @param certChainFile an X.509 certificate chain file in PEM format.
542      *                      {@code null} to use the system default
543      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
544      *                            that verifies the certificates sent from servers.
545      *                            {@code null} to use the default.
546      *
547      * @return a new client-side {@link SslContext}
548      * @deprecated Replaced by {@link SslContextBuilder}
549      */
550     @Deprecated
551     public static SslContext newClientContext(
552             File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
553         return newClientContext(null, certChainFile, trustManagerFactory);
554     }
555 
556     /**
557      * Creates a new client-side {@link SslContext}.
558      *
559      * @param certChainFile an X.509 certificate chain file in PEM format.
560      *                      {@code null} to use the system default
561      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
562      *                            that verifies the certificates sent from servers.
563      *                            {@code null} to use the default.
564      * @param ciphers the cipher suites to enable, in the order of preference.
565      *                {@code null} to use the default cipher suites.
566      * @param nextProtocols the application layer protocols to accept, in the order of preference.
567      *                      {@code null} to disable TLS NPN/ALPN extension.
568      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
569      *                         {@code 0} to use the default value.
570      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
571      *                       {@code 0} to use the default value.
572      *
573      * @return a new client-side {@link SslContext}
574      * @deprecated Replaced by {@link SslContextBuilder}
575      */
576     @Deprecated
577     public static SslContext newClientContext(
578             File certChainFile, TrustManagerFactory trustManagerFactory,
579             Iterable<String> ciphers, Iterable<String> nextProtocols,
580             long sessionCacheSize, long sessionTimeout) throws SSLException {
581         return newClientContext(
582                 null, certChainFile, trustManagerFactory,
583                 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
584     }
585 
586     /**
587      * Creates a new client-side {@link SslContext}.
588      *
589      * @param certChainFile an X.509 certificate chain file in PEM format.
590      *                      {@code null} to use the system default
591      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
592      *                            that verifies the certificates sent from servers.
593      *                            {@code null} to use the default.
594      * @param ciphers the cipher suites to enable, in the order of preference.
595      *                {@code null} to use the default cipher suites.
596      * @param cipherFilter a filter to apply over the supplied list of ciphers
597      * @param apn Provides a means to configure parameters related to application protocol negotiation.
598      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
599      *                         {@code 0} to use the default value.
600      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
601      *                       {@code 0} to use the default value.
602      *
603      * @return a new client-side {@link SslContext}
604      * @deprecated Replaced by {@link SslContextBuilder}
605      */
606     @Deprecated
607     public static SslContext newClientContext(
608             File certChainFile, TrustManagerFactory trustManagerFactory,
609             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
610             long sessionCacheSize, long sessionTimeout) throws SSLException {
611         return newClientContext(
612                 null, certChainFile, trustManagerFactory,
613                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
614     }
615 
616     /**
617      * Creates a new client-side {@link SslContext}.
618      *
619      * @param provider the {@link SslContext} implementation to use.
620      *                 {@code null} to use the current default one.
621      *
622      * @return a new client-side {@link SslContext}
623      * @deprecated Replaced by {@link SslContextBuilder}
624      */
625     @Deprecated
626     public static SslContext newClientContext(SslProvider provider) throws SSLException {
627         return newClientContext(provider, null, null);
628     }
629 
630     /**
631      * Creates a new client-side {@link SslContext}.
632      *
633      * @param provider the {@link SslContext} implementation to use.
634      *                 {@code null} to use the current default one.
635      * @param certChainFile an X.509 certificate chain file in PEM format.
636      *                      {@code null} to use the system default
637      *
638      * @return a new client-side {@link SslContext}
639      * @deprecated Replaced by {@link SslContextBuilder}
640      */
641     @Deprecated
642     public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
643         return newClientContext(provider, certChainFile, null);
644     }
645 
646     /**
647      * Creates a new client-side {@link SslContext}.
648      *
649      * @param provider the {@link SslContext} implementation to use.
650      *                 {@code null} to use the current default one.
651      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
652      *                            that verifies the certificates sent from servers.
653      *                            {@code null} to use the default.
654      *
655      * @return a new client-side {@link SslContext}
656      * @deprecated Replaced by {@link SslContextBuilder}
657      */
658     @Deprecated
659     public static SslContext newClientContext(
660             SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
661         return newClientContext(provider, null, trustManagerFactory);
662     }
663 
664     /**
665      * Creates a new client-side {@link SslContext}.
666      *
667      * @param provider the {@link SslContext} implementation to use.
668      *                 {@code null} to use the current default one.
669      * @param certChainFile an X.509 certificate chain file in PEM format.
670      *                      {@code null} to use the system default
671      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
672      *                            that verifies the certificates sent from servers.
673      *                            {@code null} to use the default.
674      *
675      * @return a new client-side {@link SslContext}
676      * @deprecated Replaced by {@link SslContextBuilder}
677      */
678     @Deprecated
679     public static SslContext newClientContext(
680             SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
681         return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
682                 null, 0, 0);
683     }
684 
685     /**
686      * Creates a new client-side {@link SslContext}.
687      *
688      * @param provider the {@link SslContext} implementation to use.
689      *                 {@code null} to use the current default one.
690      * @param certChainFile an X.509 certificate chain file in PEM format.
691      *                      {@code null} to use the system default
692      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
693      *                            that verifies the certificates sent from servers.
694      *                            {@code null} to use the default.
695      * @param ciphers the cipher suites to enable, in the order of preference.
696      *                {@code null} to use the default cipher suites.
697      * @param nextProtocols the application layer protocols to accept, in the order of preference.
698      *                      {@code null} to disable TLS NPN/ALPN extension.
699      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
700      *                         {@code 0} to use the default value.
701      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
702      *                       {@code 0} to use the default value.
703      *
704      * @return a new client-side {@link SslContext}
705      * @deprecated Replaced by {@link SslContextBuilder}
706      */
707     @Deprecated
708     public static SslContext newClientContext(
709             SslProvider provider,
710             File certChainFile, TrustManagerFactory trustManagerFactory,
711             Iterable<String> ciphers, Iterable<String> nextProtocols,
712             long sessionCacheSize, long sessionTimeout) throws SSLException {
713         return newClientContext(
714                 provider, certChainFile, trustManagerFactory, null, null, null, null,
715                 ciphers, IdentityCipherSuiteFilter.INSTANCE,
716                 toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
717     }
718 
719     /**
720      * Creates a new client-side {@link SslContext}.
721      *
722      * @param provider the {@link SslContext} implementation to use.
723      *                 {@code null} to use the current default one.
724      * @param certChainFile an X.509 certificate chain file in PEM format.
725      *                      {@code null} to use the system default
726      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
727      *                            that verifies the certificates sent from servers.
728      *                            {@code null} to use the default.
729      * @param ciphers the cipher suites to enable, in the order of preference.
730      *                {@code null} to use the default cipher suites.
731      * @param cipherFilter a filter to apply over the supplied list of ciphers
732      * @param apn Provides a means to configure parameters related to application protocol negotiation.
733      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
734      *                         {@code 0} to use the default value.
735      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
736      *                       {@code 0} to use the default value.
737      *
738      * @return a new client-side {@link SslContext}
739      * @deprecated Replaced by {@link SslContextBuilder}
740      */
741     @Deprecated
742     public static SslContext newClientContext(
743             SslProvider provider,
744             File certChainFile, TrustManagerFactory trustManagerFactory,
745             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
746             long sessionCacheSize, long sessionTimeout) throws SSLException {
747 
748         return newClientContext(
749                 provider, certChainFile, trustManagerFactory, null, null, null, null,
750                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
751     }
752 
753     /**
754      * Creates a new client-side {@link SslContext}.
755      * @param provider the {@link SslContext} implementation to use.
756      *                 {@code null} to use the current default one.
757      * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
758      *                      {@code null} to use the system default
759      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
760      *                            that verifies the certificates sent from servers.
761      *                            {@code null} to use the default or the results of parsing
762      *                            {@code trustCertCollectionFile}.
763      *                            This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
764      * @param keyCertChainFile an X.509 certificate chain file in PEM format.
765      *                      This provides the public key for mutual authentication.
766      *                      {@code null} to use the system default
767      * @param keyFile a PKCS#8 private key file in PEM format.
768      *                      This provides the private key for mutual authentication.
769      *                      {@code null} for no mutual authentication.
770      * @param keyPassword the password of the {@code keyFile}.
771      *                    {@code null} if it's not password-protected.
772      *                    Ignored if {@code keyFile} is {@code null}.
773      * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
774      *                          that is used to encrypt data being sent to servers.
775      *                          {@code null} to use the default or the results of parsing
776      *                          {@code keyCertChainFile} and {@code keyFile}.
777      *                          This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
778      * @param ciphers the cipher suites to enable, in the order of preference.
779      *                {@code null} to use the default cipher suites.
780      * @param cipherFilter a filter to apply over the supplied list of ciphers
781      * @param apn Provides a means to configure parameters related to application protocol negotiation.
782      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
783      *                         {@code 0} to use the default value.
784      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
785      *                       {@code 0} to use the default value.
786      *
787      * @return a new client-side {@link SslContext}
788      * @deprecated Replaced by {@link SslContextBuilder}
789      */
790     @Deprecated
791     public static SslContext newClientContext(
792         SslProvider provider,
793         File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
794         File keyCertChainFile, File keyFile, String keyPassword,
795         KeyManagerFactory keyManagerFactory,
796         Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
797         long sessionCacheSize, long sessionTimeout) throws SSLException {
798         try {
799             return newClientContextInternal(provider, null,
800                                             toX509Certificates(trustCertCollectionFile), trustManagerFactory,
801                                             toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
802                                             keyPassword, keyManagerFactory, ciphers, cipherFilter,
803                                             apn, null, sessionCacheSize, sessionTimeout, false,
804                                             KeyStore.getDefaultType());
805         } catch (Exception e) {
806             if (e instanceof SSLException) {
807                 throw (SSLException) e;
808             }
809             throw new SSLException("failed to initialize the client-side SSL context", e);
810         }
811     }
812 
813     static SslContext newClientContextInternal(
814             SslProvider provider,
815             Provider sslContextProvider,
816             X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
817             X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
818             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
819             long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStoreType,
820             Map.Entry<SslContextOption<?>, Object>... options) throws SSLException {
821         if (provider == null) {
822             provider = defaultClientProvider();
823         }
824         switch (provider) {
825             case JDK:
826                 if (enableOcsp) {
827                     throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
828                 }
829                 try {
830                     return new JdkSslClientContext(sslContextProvider,
831                             trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
832                             keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize,
833                             sessionTimeout, keyStoreType);
834                 } catch (SSLException e) {
835                     throw e;
836                 } catch (Exception e) {
837                     throw new SSLException("Failed to build SslContext", e);
838                 }
839         case OPENSSL:
840                 verifyNullSslContextProvider(provider, sslContextProvider);
841                 OpenSsl.ensureAvailability();
842                 return new OpenSslClientContext(
843                         trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
844                         keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
845                         enableOcsp, keyStoreType, options);
846             case OPENSSL_REFCNT:
847                 verifyNullSslContextProvider(provider, sslContextProvider);
848                 OpenSsl.ensureAvailability();
849                 return new ReferenceCountedOpenSslClientContext(
850                         trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
851                         keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
852                         enableOcsp, keyStoreType, options);
853             default:
854                 throw new Error(provider.toString());
855         }
856     }
857 
858     static ApplicationProtocolConfig toApplicationProtocolConfig(Iterable<String> nextProtocols) {
859         ApplicationProtocolConfig apn;
860         if (nextProtocols == null) {
861             apn = ApplicationProtocolConfig.DISABLED;
862         } else {
863             apn = new ApplicationProtocolConfig(
864                     Protocol.NPN_AND_ALPN, SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
865                     SelectedListenerFailureBehavior.ACCEPT, nextProtocols);
866         }
867         return apn;
868     }
869 
870     /**
871      * Creates a new instance (startTls set to {@code false}).
872      */
873     protected SslContext() {
874         this(false);
875     }
876 
877     /**
878      * Creates a new instance.
879      */
880     protected SslContext(boolean startTls) {
881         this.startTls = startTls;
882     }
883 
884     /**
885      * Returns the {@link AttributeMap} that belongs to this {@link SslContext} .
886      */
887     public final AttributeMap attributes() {
888         return attributes;
889     }
890 
891     /**
892      * Returns {@code true} if and only if this context is for server-side.
893      */
894     public final boolean isServer() {
895         return !isClient();
896     }
897 
898     /**
899      * Returns the {@code true} if and only if this context is for client-side.
900      */
901     public abstract boolean isClient();
902 
903     /**
904      * Returns the list of enabled cipher suites, in the order of preference.
905      */
906     public abstract List<String> cipherSuites();
907 
908     /**
909      * Returns the size of the cache used for storing SSL session objects.
910      */
911     public long sessionCacheSize() {
912         return sessionContext().getSessionCacheSize();
913     }
914 
915     /**
916      * Returns the timeout for the cached SSL session objects, in seconds.
917      */
918     public long sessionTimeout() {
919         return sessionContext().getSessionTimeout();
920     }
921 
922     /**
923      * @deprecated Use {@link #applicationProtocolNegotiator()} instead.
924      */
925     @Deprecated
926     public final List<String> nextProtocols() {
927         return applicationProtocolNegotiator().protocols();
928     }
929 
930     /**
931      * Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
932      */
933     public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
934 
935     /**
936      * Creates a new {@link SSLEngine}.
937      * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
938      * wrap in a {@link SslHandler} and insert it into a pipeline. See {@link #newHandler(BufferAllocator)}.
939      * @return a new {@link SSLEngine}
940      */
941     public abstract SSLEngine newEngine(BufferAllocator alloc);
942 
943     /**
944      * Creates a new {@link SSLEngine} using advisory peer information.
945      * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
946      * wrap in a {@link SslHandler} and insert it into a pipeline.
947      * See {@link #newHandler(BufferAllocator, String, int)}.
948      * @param peerHost the non-authoritative name of the host
949      * @param peerPort the non-authoritative port
950      *
951      * @return a new {@link SSLEngine}
952      */
953     public abstract SSLEngine newEngine(BufferAllocator alloc, String peerHost, int peerPort);
954 
955     /**
956      * Returns the {@link SSLSessionContext} object held by this context.
957      */
958     public abstract SSLSessionContext sessionContext();
959 
960     /**
961      * Create a new SslHandler.
962      * @see #newHandler(BufferAllocator, Executor)
963      */
964     public final SslHandler newHandler(BufferAllocator alloc) {
965         return newHandler(alloc, startTls);
966     }
967 
968     /**
969      * Create a new SslHandler.
970      * @see #newHandler(BufferAllocator)
971      */
972     protected SslHandler newHandler(BufferAllocator alloc, boolean startTls) {
973         return new SslHandler(newEngine(alloc), startTls);
974     }
975 
976     /**
977      * Creates a new {@link SslHandler}.
978      * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
979      * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
980      * memory!
981      * <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
982      * <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
983      * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
984      * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
985      * <pre>
986      * SSLEngine sslEngine = sslHandler.engine();
987      * SSLParameters sslParameters = sslEngine.getSSLParameters();
988      * // only available since Java 7
989      * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
990      * sslEngine.setSSLParameters(sslParameters);
991      * </pre>
992      * <p>
993      * The underlying {@link SSLEngine} may not follow the restrictions imposed by the
994      * <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
995      * limits wrap/unwrap to operate on a single SSL/TLS packet.
996      * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate Buffer objects.
997      * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
998      *                              {@link SSLEngine#getDelegatedTask()}.
999      * @return a new {@link SslHandler}
1000      */
1001     public SslHandler newHandler(BufferAllocator alloc, Executor delegatedTaskExecutor) {
1002         return newHandler(alloc, startTls, delegatedTaskExecutor);
1003     }
1004 
1005     /**
1006      * Create a new SslHandler.
1007      * @see #newHandler(BufferAllocator, String, int, boolean, Executor)
1008      */
1009     protected SslHandler newHandler(BufferAllocator alloc, boolean startTls, Executor executor) {
1010         return new SslHandler(newEngine(alloc), startTls, executor);
1011     }
1012 
1013     /**
1014      * Creates a new {@link SslHandler}
1015      *
1016      * @see #newHandler(BufferAllocator, String, int, Executor)
1017      */
1018     public final SslHandler newHandler(BufferAllocator alloc, String peerHost, int peerPort) {
1019         return newHandler(alloc, peerHost, peerPort, startTls);
1020     }
1021 
1022     /**
1023      * Create a new SslHandler.
1024      * @see #newHandler(BufferAllocator, String, int, boolean, Executor)
1025      */
1026     protected SslHandler newHandler(BufferAllocator alloc, String peerHost, int peerPort, boolean startTls) {
1027         return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls);
1028     }
1029 
1030     /**
1031      * Creates a new {@link SslHandler} with advisory peer information.
1032      * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
1033      * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
1034      * memory!
1035      * <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
1036      * <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
1037      * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
1038      * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
1039      * <pre>
1040      * SSLEngine sslEngine = sslHandler.engine();
1041      * SSLParameters sslParameters = sslEngine.getSSLParameters();
1042      * // only available since Java 7
1043      * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
1044      * sslEngine.setSSLParameters(sslParameters);
1045      * </pre>
1046      * <p>
1047      * The underlying {@link SSLEngine} may not follow the restrictions imposed by the
1048      * <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
1049      * limits wrap/unwrap to operate on a single SSL/TLS packet.
1050      * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate Buffer objects.
1051      * @param peerHost the non-authoritative name of the host
1052      * @param peerPort the non-authoritative port
1053      * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
1054      *                              {@link SSLEngine#getDelegatedTask()}.
1055      *
1056      * @return a new {@link SslHandler}
1057      */
1058     public SslHandler newHandler(BufferAllocator alloc, String peerHost, int peerPort,
1059                                  Executor delegatedTaskExecutor) {
1060         return newHandler(alloc, peerHost, peerPort, startTls, delegatedTaskExecutor);
1061     }
1062 
1063     protected SslHandler newHandler(BufferAllocator alloc, String peerHost, int peerPort, boolean startTls,
1064                                     Executor delegatedTaskExecutor) {
1065         return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls, delegatedTaskExecutor);
1066     }
1067 
1068     /**
1069      * Generates a key specification for an (encrypted) private key.
1070      *
1071      * @param password characters, if {@code null} an unencrypted key is assumed
1072      * @param key bytes of the DER encoded private key
1073      *
1074      * @return a key specification
1075      *
1076      * @throws IOException if parsing {@code key} fails
1077      * @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unknown
1078      * @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unknown
1079      * @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
1080      * @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
1081      *                             {@code key}
1082      * @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
1083      */
1084     @Deprecated
1085     protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
1086             throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
1087             InvalidKeyException, InvalidAlgorithmParameterException {
1088 
1089         if (password == null) {
1090             return new PKCS8EncodedKeySpec(key);
1091         }
1092 
1093         EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
1094         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
1095         PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
1096         SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
1097 
1098         Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
1099         cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
1100 
1101         return encryptedPrivateKeyInfo.getKeySpec(cipher);
1102     }
1103 
1104     /**
1105      * Generates a new {@link KeyStore}.
1106      *
1107      * @param certChain an X.509 certificate chain
1108      * @param key a PKCS#8 private key
1109      * @param keyPasswordChars the password of the {@code keyFile}.
1110      *                    {@code null} if it's not password-protected.
1111      * @param keyStoreType The KeyStore Type you want to use
1112      * @return generated {@link KeyStore}.
1113      */
1114     protected static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key,
1115                                   char[] keyPasswordChars, String keyStoreType)
1116             throws KeyStoreException, NoSuchAlgorithmException,
1117                    CertificateException, IOException {
1118         if (keyStoreType == null) {
1119             keyStoreType = KeyStore.getDefaultType();
1120         }
1121         KeyStore ks = KeyStore.getInstance(keyStoreType);
1122         ks.load(null, null);
1123         ks.setKeyEntry(ALIAS, key, keyPasswordChars, certChain);
1124         return ks;
1125     }
1126 
1127     protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException,
1128                                                                 NoSuchPaddingException, InvalidKeySpecException,
1129                                                                 InvalidAlgorithmParameterException,
1130                                                                 KeyException, IOException {
1131         if (keyFile == null) {
1132             return null;
1133         }
1134         return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyFile), keyPassword);
1135     }
1136 
1137     protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword)
1138                                                                 throws NoSuchAlgorithmException,
1139                                                                 NoSuchPaddingException, InvalidKeySpecException,
1140                                                                 InvalidAlgorithmParameterException,
1141                                                                 KeyException, IOException {
1142         if (keyInputStream == null) {
1143             return null;
1144         }
1145         return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword);
1146     }
1147 
1148     private static PrivateKey getPrivateKeyFromByteBuffer(Buffer encodedKeyBuf, String keyPassword)
1149             throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
1150             InvalidAlgorithmParameterException, KeyException, IOException {
1151 
1152         byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
1153         encodedKeyBuf.readBytes(encodedKey, 0, encodedKey.length).close();
1154 
1155         PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(
1156                 keyPassword == null ? null : keyPassword.toCharArray(), encodedKey);
1157         try {
1158             return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
1159         } catch (InvalidKeySpecException ignore) {
1160             try {
1161                 return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
1162             } catch (InvalidKeySpecException ignore2) {
1163                 try {
1164                     return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
1165                 } catch (InvalidKeySpecException e) {
1166                     throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
1167                 }
1168             }
1169         }
1170     }
1171 
1172     /**
1173      * Build a {@link TrustManagerFactory} from a certificate chain file.
1174      * @param certChainFile The certificate file to build from.
1175      * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
1176      * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
1177      */
1178     @Deprecated
1179     protected static TrustManagerFactory buildTrustManagerFactory(
1180             File certChainFile, TrustManagerFactory trustManagerFactory)
1181             throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1182         return buildTrustManagerFactory(certChainFile, trustManagerFactory, null);
1183     }
1184 
1185     /**
1186      * Build a {@link TrustManagerFactory} from a certificate chain file.
1187      * @param certChainFile The certificate file to build from.
1188      * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
1189      * @param keyType The KeyStore Type you want to use
1190      * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
1191      */
1192     protected static TrustManagerFactory buildTrustManagerFactory(
1193             File certChainFile, TrustManagerFactory trustManagerFactory, String keyType)
1194             throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1195         X509Certificate[] x509Certs = toX509Certificates(certChainFile);
1196 
1197         return buildTrustManagerFactory(x509Certs, trustManagerFactory, keyType);
1198     }
1199 
1200     protected static X509Certificate[] toX509Certificates(File file) throws CertificateException {
1201         if (file == null) {
1202             return null;
1203         }
1204         return getCertificatesFromBuffers(PemReader.readCertificates(file));
1205     }
1206 
1207     protected static X509Certificate[] toX509Certificates(InputStream in) throws CertificateException {
1208         if (in == null) {
1209             return null;
1210         }
1211         return getCertificatesFromBuffers(PemReader.readCertificates(in));
1212     }
1213 
1214     private static X509Certificate[] getCertificatesFromBuffers(Buffer[] certs) throws CertificateException {
1215         CertificateFactory cf = CertificateFactory.getInstance("X.509");
1216         X509Certificate[] x509Certs = new X509Certificate[certs.length];
1217 
1218         for (int i = 0; i < certs.length; i++) {
1219             try (InputStream is = new BufferInputStream(certs[i].send())) {
1220                 x509Certs[i] = (X509Certificate) cf.generateCertificate(is);
1221             } catch (IOException e) {
1222                 // This is thrown from is.close(). It's not expected to happen, but re-throw in case it does.
1223                 throw new UncheckedIOException(e);
1224             }
1225         }
1226         return x509Certs;
1227     }
1228 
1229     protected static TrustManagerFactory buildTrustManagerFactory(
1230             X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory, String keyStoreType)
1231             throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1232         if (keyStoreType == null) {
1233             keyStoreType = KeyStore.getDefaultType();
1234         }
1235         final KeyStore ks = KeyStore.getInstance(keyStoreType);
1236         ks.load(null, null);
1237 
1238         int i = 1;
1239         for (X509Certificate cert: certCollection) {
1240             String alias = Integer.toString(i);
1241             ks.setCertificateEntry(alias, cert);
1242             i++;
1243         }
1244 
1245         // Set up trust manager factory to use our key store.
1246         if (trustManagerFactory == null) {
1247             trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
1248         }
1249         trustManagerFactory.init(ks);
1250 
1251         return trustManagerFactory;
1252     }
1253 
1254     static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException {
1255         try {
1256             return toPrivateKey(keyFile, keyPassword);
1257         } catch (Exception e) {
1258             throw new SSLException(e);
1259         }
1260     }
1261 
1262     static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException {
1263         try {
1264             return toX509Certificates(file);
1265         } catch (CertificateException e) {
1266             throw new SSLException(e);
1267         }
1268     }
1269 
1270     protected static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile,
1271                                                     String keyAlgorithm, PrivateKey key,
1272                                                     String keyPassword, KeyManagerFactory kmf,
1273                                                     String keyStore)
1274             throws KeyStoreException, NoSuchAlgorithmException, IOException,
1275             CertificateException, UnrecoverableKeyException {
1276         if (keyAlgorithm == null) {
1277             keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1278         }
1279         char[] keyPasswordChars = keyStorePassword(keyPassword);
1280         KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars, keyStore);
1281         return buildKeyManagerFactory(ks, keyAlgorithm, keyPasswordChars, kmf);
1282     }
1283 
1284     static KeyManagerFactory buildKeyManagerFactory(KeyStore ks,
1285                                                     String keyAlgorithm,
1286                                                     char[] keyPasswordChars, KeyManagerFactory kmf)
1287             throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
1288         // Set up key manager factory to use our key store
1289         if (kmf == null) {
1290             if (keyAlgorithm == null) {
1291                 keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1292             }
1293             kmf = KeyManagerFactory.getInstance(keyAlgorithm);
1294         }
1295         kmf.init(ks, keyPasswordChars);
1296 
1297         return kmf;
1298     }
1299 
1300     static char[] keyStorePassword(String keyPassword) {
1301         return keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray();
1302     }
1303 }