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