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 credentials);
531 case OPENSSL_REFCNT:
532 verifyNullSslContextProvider(provider, sslContextProvider);
533 return new ReferenceCountedOpenSslServerContext(
534 trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
535 keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
536 clientAuth, protocols, startTls, enableOcsp, keyStoreType, resumptionController, ctxOptions,
537 credentials);
538 default:
539 throw new Error("Unexpected provider: " + provider);
540 }
541 }
542
543 private static void verifyNullSslContextProvider(SslProvider provider, Provider sslContextProvider) {
544 if (sslContextProvider != null) {
545 throw new IllegalArgumentException("Java Security Provider unsupported for SslProvider: " + provider);
546 }
547 }
548
549 /**
550 * Creates a new client-side {@link SslContext}.
551 *
552 * @return a new client-side {@link SslContext}
553 * @deprecated Replaced by {@link SslContextBuilder}
554 */
555 @Deprecated
556 public static SslContext newClientContext() throws SSLException {
557 return newClientContext(null, null, null);
558 }
559
560 /**
561 * Creates a new client-side {@link SslContext}.
562 *
563 * @param certChainFile an X.509 certificate chain file in PEM format
564 *
565 * @return a new client-side {@link SslContext}
566 * @deprecated Replaced by {@link SslContextBuilder}
567 */
568 @Deprecated
569 public static SslContext newClientContext(File certChainFile) throws SSLException {
570 return newClientContext(null, certChainFile);
571 }
572
573 /**
574 * Creates a new client-side {@link SslContext}.
575 *
576 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
577 * that verifies the certificates sent from servers.
578 * {@code null} to use the default.
579 *
580 * @return a new client-side {@link SslContext}
581 * @deprecated Replaced by {@link SslContextBuilder}
582 */
583 @Deprecated
584 public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
585 return newClientContext(null, null, trustManagerFactory);
586 }
587
588 /**
589 * Creates a new client-side {@link SslContext}.
590 *
591 * @param certChainFile an X.509 certificate chain file in PEM format.
592 * {@code null} to use the system default
593 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
594 * that verifies the certificates sent from servers.
595 * {@code null} to use the default.
596 *
597 * @return a new client-side {@link SslContext}
598 * @deprecated Replaced by {@link SslContextBuilder}
599 */
600 @Deprecated
601 public static SslContext newClientContext(
602 File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
603 return newClientContext(null, certChainFile, trustManagerFactory);
604 }
605
606 /**
607 * Creates a new client-side {@link SslContext}.
608 *
609 * @param certChainFile an X.509 certificate chain file in PEM format.
610 * {@code null} to use the system default
611 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
612 * that verifies the certificates sent from servers.
613 * {@code null} to use the default.
614 * @param ciphers the cipher suites to enable, in the order of preference.
615 * {@code null} to use the default cipher suites.
616 * @param nextProtocols the application layer protocols to accept, in the order of preference.
617 * {@code null} to disable TLS NPN/ALPN extension.
618 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
619 * {@code 0} to use the default value.
620 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
621 * {@code 0} to use the default value.
622 *
623 * @return a new client-side {@link SslContext}
624 * @deprecated Replaced by {@link SslContextBuilder}
625 */
626 @Deprecated
627 public static SslContext newClientContext(
628 File certChainFile, TrustManagerFactory trustManagerFactory,
629 Iterable<String> ciphers, Iterable<String> nextProtocols,
630 long sessionCacheSize, long sessionTimeout) throws SSLException {
631 return newClientContext(
632 null, certChainFile, trustManagerFactory,
633 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
634 }
635
636 /**
637 * Creates a new client-side {@link SslContext}.
638 *
639 * @param certChainFile an X.509 certificate chain file in PEM format.
640 * {@code null} to use the system default
641 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
642 * that verifies the certificates sent from servers.
643 * {@code null} to use the default.
644 * @param ciphers the cipher suites to enable, in the order of preference.
645 * {@code null} to use the default cipher suites.
646 * @param cipherFilter a filter to apply over the supplied list of ciphers
647 * @param apn Provides a means to configure parameters related to application protocol negotiation.
648 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
649 * {@code 0} to use the default value.
650 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
651 * {@code 0} to use the default value.
652 *
653 * @return a new client-side {@link SslContext}
654 * @deprecated Replaced by {@link SslContextBuilder}
655 */
656 @Deprecated
657 public static SslContext newClientContext(
658 File certChainFile, TrustManagerFactory trustManagerFactory,
659 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
660 long sessionCacheSize, long sessionTimeout) throws SSLException {
661 return newClientContext(
662 null, certChainFile, trustManagerFactory,
663 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
664 }
665
666 /**
667 * Creates a new client-side {@link SslContext}.
668 *
669 * @param provider the {@link SslContext} implementation to use.
670 * {@code null} to use the current default one.
671 *
672 * @return a new client-side {@link SslContext}
673 * @deprecated Replaced by {@link SslContextBuilder}
674 */
675 @Deprecated
676 public static SslContext newClientContext(SslProvider provider) throws SSLException {
677 return newClientContext(provider, null, null);
678 }
679
680 /**
681 * Creates a new client-side {@link SslContext}.
682 *
683 * @param provider the {@link SslContext} implementation to use.
684 * {@code null} to use the current default one.
685 * @param certChainFile an X.509 certificate chain file in PEM format.
686 * {@code null} to use the system default
687 *
688 * @return a new client-side {@link SslContext}
689 * @deprecated Replaced by {@link SslContextBuilder}
690 */
691 @Deprecated
692 public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
693 return newClientContext(provider, certChainFile, null);
694 }
695
696 /**
697 * Creates a new client-side {@link SslContext}.
698 *
699 * @param provider the {@link SslContext} implementation to use.
700 * {@code null} to use the current default one.
701 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
702 * that verifies the certificates sent from servers.
703 * {@code null} to use the default.
704 *
705 * @return a new client-side {@link SslContext}
706 * @deprecated Replaced by {@link SslContextBuilder}
707 */
708 @Deprecated
709 public static SslContext newClientContext(
710 SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
711 return newClientContext(provider, null, trustManagerFactory);
712 }
713
714 /**
715 * Creates a new client-side {@link SslContext}.
716 *
717 * @param provider the {@link SslContext} implementation to use.
718 * {@code null} to use the current default one.
719 * @param certChainFile an X.509 certificate chain file in PEM format.
720 * {@code null} to use the system default
721 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
722 * that verifies the certificates sent from servers.
723 * {@code null} to use the default.
724 *
725 * @return a new client-side {@link SslContext}
726 * @deprecated Replaced by {@link SslContextBuilder}
727 */
728 @Deprecated
729 public static SslContext newClientContext(
730 SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
731 return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
732 null, 0, 0);
733 }
734
735 /**
736 * Creates a new client-side {@link SslContext}.
737 *
738 * @param provider the {@link SslContext} implementation to use.
739 * {@code null} to use the current default one.
740 * @param certChainFile an X.509 certificate chain file in PEM format.
741 * {@code null} to use the system default
742 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
743 * that verifies the certificates sent from servers.
744 * {@code null} to use the default.
745 * @param ciphers the cipher suites to enable, in the order of preference.
746 * {@code null} to use the default cipher suites.
747 * @param nextProtocols the application layer protocols to accept, in the order of preference.
748 * {@code null} to disable TLS NPN/ALPN extension.
749 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
750 * {@code 0} to use the default value.
751 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
752 * {@code 0} to use the default value.
753 *
754 * @return a new client-side {@link SslContext}
755 * @deprecated Replaced by {@link SslContextBuilder}
756 */
757 @Deprecated
758 public static SslContext newClientContext(
759 SslProvider provider,
760 File certChainFile, TrustManagerFactory trustManagerFactory,
761 Iterable<String> ciphers, Iterable<String> nextProtocols,
762 long sessionCacheSize, long sessionTimeout) throws SSLException {
763 return newClientContext(
764 provider, certChainFile, trustManagerFactory, null, null, null, null,
765 ciphers, IdentityCipherSuiteFilter.INSTANCE,
766 toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
767 }
768
769 /**
770 * Creates a new client-side {@link SslContext}.
771 *
772 * @param provider the {@link SslContext} implementation to use.
773 * {@code null} to use the current default one.
774 * @param certChainFile an X.509 certificate chain file in PEM format.
775 * {@code null} to use the system default
776 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
777 * that verifies the certificates sent from servers.
778 * {@code null} to use the default.
779 * @param ciphers the cipher suites to enable, in the order of preference.
780 * {@code null} to use the default cipher suites.
781 * @param cipherFilter a filter to apply over the supplied list of ciphers
782 * @param apn Provides a means to configure parameters related to application protocol negotiation.
783 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
784 * {@code 0} to use the default value.
785 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
786 * {@code 0} to use the default value.
787 *
788 * @return a new client-side {@link SslContext}
789 * @deprecated Replaced by {@link SslContextBuilder}
790 */
791 @Deprecated
792 public static SslContext newClientContext(
793 SslProvider provider,
794 File certChainFile, TrustManagerFactory trustManagerFactory,
795 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
796 long sessionCacheSize, long sessionTimeout) throws SSLException {
797
798 return newClientContext(
799 provider, certChainFile, trustManagerFactory, null, null, null, null,
800 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
801 }
802
803 /**
804 * Creates a new client-side {@link SslContext}.
805 * @param provider the {@link SslContext} implementation to use.
806 * {@code null} to use the current default one.
807 * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
808 * {@code null} to use the system default
809 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
810 * that verifies the certificates sent from servers.
811 * {@code null} to use the default or the results of parsing
812 * {@code trustCertCollectionFile}.
813 * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
814 * @param keyCertChainFile an X.509 certificate chain file in PEM format.
815 * This provides the public key for mutual authentication.
816 * {@code null} to use the system default
817 * @param keyFile a PKCS#8 private key file in PEM format.
818 * This provides the private key for mutual authentication.
819 * {@code null} for no mutual authentication.
820 * @param keyPassword the password of the {@code keyFile}.
821 * {@code null} if it's not password-protected.
822 * Ignored if {@code keyFile} is {@code null}.
823 * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
824 * that is used to encrypt data being sent to servers.
825 * {@code null} to use the default or the results of parsing
826 * {@code keyCertChainFile} and {@code keyFile}.
827 * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
828 * @param ciphers the cipher suites to enable, in the order of preference.
829 * {@code null} to use the default cipher suites.
830 * @param cipherFilter a filter to apply over the supplied list of ciphers
831 * @param apn Provides a means to configure parameters related to application protocol negotiation.
832 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
833 * {@code 0} to use the default value.
834 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
835 * {@code 0} to use the default value.
836 *
837 * @return a new client-side {@link SslContext}
838 * @deprecated Replaced by {@link SslContextBuilder}
839 */
840 @Deprecated
841 public static SslContext newClientContext(
842 SslProvider provider,
843 File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
844 File keyCertChainFile, File keyFile, String keyPassword,
845 KeyManagerFactory keyManagerFactory,
846 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
847 long sessionCacheSize, long sessionTimeout) throws SSLException {
848 try {
849 return newClientContextInternal(provider, null,
850 toX509Certificates(trustCertCollectionFile), trustManagerFactory,
851 toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
852 keyPassword, keyManagerFactory, ciphers, cipherFilter,
853 apn, null, sessionCacheSize, sessionTimeout, false,
854 null, KeyStore.getDefaultType(),
855 defaultEndpointVerificationAlgorithm,
856 Collections.emptyList(), null, null);
857 } catch (Exception e) {
858 if (e instanceof SSLException) {
859 throw (SSLException) e;
860 }
861 throw new SSLException("failed to initialize the client-side SSL context", e);
862 }
863 }
864
865 static SslContext newClientContextInternal(
866 SslProvider provider,
867 Provider sslContextProvider,
868 X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
869 X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
870 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
871 long sessionCacheSize, long sessionTimeout, boolean enableOcsp,
872 SecureRandom secureRandom, String keyStoreType, String endpointIdentificationAlgorithm,
873 List<SNIServerName> serverNames,
874 Map.Entry<SslContextOption<?>, Object>[] options,
875 List<OpenSslCredential> credentials) throws SSLException {
876 return newClientContextInternal(provider, sslContextProvider, trustCert, trustManagerFactory, keyCertChain, key,
877 keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
878 false, enableOcsp, secureRandom, keyStoreType, endpointIdentificationAlgorithm, serverNames, options,
879 credentials);
880 }
881
882 static SslContext newClientContextInternal(
883 SslProvider provider,
884 Provider sslContextProvider,
885 X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
886 X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
887 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
888 long sessionCacheSize, long sessionTimeout, boolean startTls, boolean enableOcsp,
889 SecureRandom secureRandom, String keyStoreType, String endpointIdentificationAlgorithm,
890 List<SNIServerName> serverNames,
891 Map.Entry<SslContextOption<?>, Object>[] options,
892 List<OpenSslCredential> credentials) throws SSLException {
893 if (provider == null) {
894 provider = defaultClientProvider();
895 }
896
897 ResumptionController resumptionController = new ResumptionController();
898
899 switch (provider) {
900 case JDK:
901 if (enableOcsp) {
902 throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
903 }
904 if (credentials != null && !credentials.isEmpty()) {
905 throw new IllegalArgumentException(
906 "OpenSslCredential is not supported with SslProvider.JDK. " +
907 "Use SslProvider.OPENSSL or SslProvider.OPENSSL_REFCNT instead.");
908 }
909 return new JdkSslClientContext(sslContextProvider,
910 trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
911 keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize,
912 sessionTimeout, secureRandom, keyStoreType, endpointIdentificationAlgorithm,
913 serverNames, resumptionController);
914 case OPENSSL:
915 verifyNullSslContextProvider(provider, sslContextProvider);
916 OpenSsl.ensureAvailability();
917 return new OpenSslClientContext(
918 trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
919 keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
920 startTls, enableOcsp, keyStoreType, endpointIdentificationAlgorithm, serverNames,
921 resumptionController, options, credentials);
922 case OPENSSL_REFCNT:
923 verifyNullSslContextProvider(provider, sslContextProvider);
924 OpenSsl.ensureAvailability();
925 return new ReferenceCountedOpenSslClientContext(
926 trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
927 keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
928 startTls, enableOcsp, keyStoreType, endpointIdentificationAlgorithm, serverNames,
929 resumptionController, options, credentials);
930 default:
931 throw new Error("Unexpected provider: " + provider);
932 }
933 }
934
935 static ApplicationProtocolConfig toApplicationProtocolConfig(Iterable<String> nextProtocols) {
936 ApplicationProtocolConfig apn;
937 if (nextProtocols == null) {
938 apn = ApplicationProtocolConfig.DISABLED;
939 } else {
940 apn = new ApplicationProtocolConfig(
941 Protocol.NPN_AND_ALPN, SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
942 SelectedListenerFailureBehavior.ACCEPT, nextProtocols);
943 }
944 return apn;
945 }
946
947 /**
948 * Creates a new instance (startTls set to {@code false}).
949 */
950 protected SslContext() {
951 this(false);
952 }
953
954 /**
955 * Creates a new instance.
956 */
957 protected SslContext(boolean startTls) {
958 this(startTls, null);
959 }
960
961 SslContext(boolean startTls, ResumptionController resumptionController) {
962 this.startTls = startTls;
963 this.resumptionController = resumptionController;
964 }
965
966 /**
967 * Returns the {@link AttributeMap} that belongs to this {@link SslContext} .
968 */
969 public final AttributeMap attributes() {
970 return attributes;
971 }
972
973 /**
974 * Returns {@code true} if and only if this context is for server-side.
975 */
976 public final boolean isServer() {
977 return !isClient();
978 }
979
980 /**
981 * Returns the {@code true} if and only if this context is for client-side.
982 */
983 public abstract boolean isClient();
984
985 /**
986 * Returns the list of enabled cipher suites, in the order of preference.
987 */
988 public abstract List<String> cipherSuites();
989
990 /**
991 * Returns the size of the cache used for storing SSL session objects.
992 */
993 public long sessionCacheSize() {
994 return sessionContext().getSessionCacheSize();
995 }
996
997 /**
998 * Returns the timeout for the cached SSL session objects, in seconds.
999 */
1000 public long sessionTimeout() {
1001 return sessionContext().getSessionTimeout();
1002 }
1003
1004 /**
1005 * @deprecated Use {@link #applicationProtocolNegotiator()} instead.
1006 */
1007 @Deprecated
1008 public final List<String> nextProtocols() {
1009 return applicationProtocolNegotiator().protocols();
1010 }
1011
1012 /**
1013 * Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
1014 */
1015 public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
1016
1017 /**
1018 * Creates a new {@link SSLEngine}.
1019 * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
1020 * wrap in a {@link SslHandler} and insert it into a pipeline. See {@link #newHandler(ByteBufAllocator)}.
1021 * @return a new {@link SSLEngine}
1022 */
1023 public abstract SSLEngine newEngine(ByteBufAllocator alloc);
1024
1025 /**
1026 * Creates a new {@link SSLEngine} using advisory peer information.
1027 * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
1028 * wrap in a {@link SslHandler} and insert it into a pipeline.
1029 * See {@link #newHandler(ByteBufAllocator, String, int)}.
1030 * @param peerHost the non-authoritative name of the host
1031 * @param peerPort the non-authoritative port
1032 *
1033 * @return a new {@link SSLEngine}
1034 */
1035 public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort);
1036
1037 /**
1038 * Returns the {@link SSLSessionContext} object held by this context.
1039 */
1040 public abstract SSLSessionContext sessionContext();
1041
1042 /**
1043 * Create a new SslHandler.
1044 * @see #newHandler(ByteBufAllocator, Executor)
1045 */
1046 public final SslHandler newHandler(ByteBufAllocator alloc) {
1047 return newHandler(alloc, startTls);
1048 }
1049
1050 /**
1051 * Create a new SslHandler.
1052 * @see #newHandler(ByteBufAllocator)
1053 */
1054 protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
1055 return new SslHandler(newEngine(alloc), startTls, ImmediateExecutor.INSTANCE, resumptionController);
1056 }
1057
1058 /**
1059 * Creates a new {@link SslHandler}.
1060 * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
1061 * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
1062 * memory!
1063 * <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
1064 * <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
1065 * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
1066 * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
1067 * <pre>
1068 * SSLEngine sslEngine = sslHandler.engine();
1069 * SSLParameters sslParameters = sslEngine.getSSLParameters();
1070 * // only available since Java 7
1071 * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
1072 * sslEngine.setSSLParameters(sslParameters);
1073 * </pre>
1074 * <p>
1075 * The underlying {@link SSLEngine} may not follow the restrictions imposed by the
1076 * <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
1077 * limits wrap/unwrap to operate on a single SSL/TLS packet.
1078 * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
1079 * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
1080 * {@link SSLEngine#getDelegatedTask()}.
1081 * @return a new {@link SslHandler}
1082 */
1083 public SslHandler newHandler(ByteBufAllocator alloc, Executor delegatedTaskExecutor) {
1084 return newHandler(alloc, startTls, delegatedTaskExecutor);
1085 }
1086
1087 /**
1088 * Create a new SslHandler.
1089 * @see #newHandler(ByteBufAllocator, String, int, boolean, Executor)
1090 */
1091 protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) {
1092 return new SslHandler(newEngine(alloc), startTls, executor, resumptionController);
1093 }
1094
1095 /**
1096 * Creates a new {@link SslHandler}
1097 *
1098 * @see #newHandler(ByteBufAllocator, String, int, Executor)
1099 */
1100 public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
1101 return newHandler(alloc, peerHost, peerPort, startTls);
1102 }
1103
1104 /**
1105 * Create a new SslHandler.
1106 * @see #newHandler(ByteBufAllocator, String, int, boolean, Executor)
1107 */
1108 protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
1109 return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls, ImmediateExecutor.INSTANCE,
1110 resumptionController);
1111 }
1112
1113 /**
1114 * Creates a new {@link SslHandler} with advisory peer information.
1115 * <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
1116 * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
1117 * memory!
1118 * <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
1119 * <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
1120 * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
1121 * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
1122 * <pre>
1123 * SSLEngine sslEngine = sslHandler.engine();
1124 * SSLParameters sslParameters = sslEngine.getSSLParameters();
1125 * // only available since Java 7
1126 * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
1127 * sslEngine.setSSLParameters(sslParameters);
1128 * </pre>
1129 * <p>
1130 * The underlying {@link SSLEngine} may not follow the restrictions imposed by the
1131 * <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
1132 * limits wrap/unwrap to operate on a single SSL/TLS packet.
1133 * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
1134 * @param peerHost the non-authoritative name of the host
1135 * @param peerPort the non-authoritative port
1136 * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
1137 * {@link SSLEngine#getDelegatedTask()}.
1138 *
1139 * @return a new {@link SslHandler}
1140 */
1141 public SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
1142 Executor delegatedTaskExecutor) {
1143 return newHandler(alloc, peerHost, peerPort, startTls, delegatedTaskExecutor);
1144 }
1145
1146 protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls,
1147 Executor delegatedTaskExecutor) {
1148 return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls, delegatedTaskExecutor,
1149 resumptionController);
1150 }
1151
1152 /**
1153 * Generates a key specification for an (encrypted) private key.
1154 *
1155 * @param password characters, if {@code null} an unencrypted key is assumed
1156 * @param key bytes of the DER encoded private key
1157 *
1158 * @return a key specification
1159 *
1160 * @throws IOException if parsing {@code key} fails
1161 * @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unknown
1162 * @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unknown
1163 * @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
1164 * @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
1165 * {@code key}
1166 * @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
1167 */
1168 @Deprecated
1169 protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
1170 throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
1171 InvalidKeyException, InvalidAlgorithmParameterException {
1172
1173 if (password == null) {
1174 return new PKCS8EncodedKeySpec(key);
1175 }
1176
1177 EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
1178 String pbeAlgorithm = getPBEAlgorithm(encryptedPrivateKeyInfo);
1179 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(pbeAlgorithm);
1180 PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
1181 SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
1182
1183 Cipher cipher = Cipher.getInstance(pbeAlgorithm);
1184 cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
1185
1186 return encryptedPrivateKeyInfo.getKeySpec(cipher);
1187 }
1188
1189 private static String getPBEAlgorithm(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo) {
1190 AlgorithmParameters parameters = encryptedPrivateKeyInfo.getAlgParameters();
1191 String algName = encryptedPrivateKeyInfo.getAlgName();
1192 // Java 8 ~ 16 returns OID_PKCS5_PBES2
1193 // Java 17+ returns PBES2
1194 if (parameters != null && (OID_PKCS5_PBES2.equals(algName) || PBES2.equals(algName))) {
1195 /*
1196 * This should be "PBEWith<prf>And<encryption>".
1197 * Relying on the toString() implementation is potentially
1198 * fragile but acceptable in this case since the JRE depends on
1199 * the toString() implementation as well.
1200 * In the future, if necessary, we can parse the value of
1201 * parameters.getEncoded() but the associated complexity and
1202 * unlikeliness of the JRE implementation changing means that
1203 * Tomcat will use to toString() approach for now.
1204 */
1205 return parameters.toString();
1206 }
1207 return encryptedPrivateKeyInfo.getAlgName();
1208 }
1209
1210 /**
1211 * Generates a new {@link KeyStore}.
1212 *
1213 * @param certChain an X.509 certificate chain
1214 * @param key a PKCS#8 private key
1215 * @param keyPasswordChars the password of the {@code keyFile}.
1216 * {@code null} if it's not password-protected.
1217 * @param keyStoreType The KeyStore Type you want to use
1218 * @return generated {@link KeyStore}.
1219 */
1220 protected static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key,
1221 char[] keyPasswordChars, String keyStoreType)
1222 throws KeyStoreException, NoSuchAlgorithmException,
1223 CertificateException, IOException {
1224 if (keyStoreType == null) {
1225 keyStoreType = KeyStore.getDefaultType();
1226 }
1227 KeyStore ks = KeyStore.getInstance(keyStoreType);
1228 ks.load(null, null);
1229 ks.setKeyEntry(ALIAS, key, keyPasswordChars, certChain);
1230 return ks;
1231 }
1232
1233 protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException,
1234 NoSuchPaddingException, InvalidKeySpecException,
1235 InvalidAlgorithmParameterException,
1236 KeyException, IOException {
1237 return toPrivateKey(keyFile, keyPassword, true);
1238 }
1239
1240 static PrivateKey toPrivateKey(File keyFile, String keyPassword, boolean tryBouncyCastle)
1241 throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
1242 InvalidAlgorithmParameterException,
1243 KeyException, IOException {
1244 if (keyFile == null) {
1245 return null;
1246 }
1247
1248 // try BC first, if this fail fallback to original key extraction process
1249 if (tryBouncyCastle && BouncyCastleUtil.isBcPkixAvailable()) {
1250 PrivateKey pk = BouncyCastlePemReader.getPrivateKey(keyFile, keyPassword);
1251 if (pk != null) {
1252 return pk;
1253 }
1254 }
1255
1256 return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyFile), keyPassword);
1257 }
1258
1259 protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword)
1260 throws NoSuchAlgorithmException,
1261 NoSuchPaddingException, InvalidKeySpecException,
1262 InvalidAlgorithmParameterException,
1263 KeyException, IOException {
1264 if (keyInputStream == null) {
1265 return null;
1266 }
1267
1268 // try BC first, if this fail fallback to original key extraction process
1269 if (BouncyCastleUtil.isBcPkixAvailable()) {
1270 if (!keyInputStream.markSupported()) {
1271 // We need an input stream that supports resetting, in case BouncyCastle fails to read.
1272 keyInputStream = new BufferedInputStream(keyInputStream);
1273 }
1274 keyInputStream.mark(1048576); // Be able to reset up to 1 MiB of data.
1275 PrivateKey pk = BouncyCastlePemReader.getPrivateKey(keyInputStream, keyPassword);
1276 if (pk != null) {
1277 return pk;
1278 }
1279 // BouncyCastle could not read the key. Reset the input stream in case the input position changed.
1280 keyInputStream.reset();
1281 }
1282
1283 return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword);
1284 }
1285
1286 private static PrivateKey getPrivateKeyFromByteBuffer(ByteBuf encodedKeyBuf, String keyPassword)
1287 throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
1288 InvalidAlgorithmParameterException, KeyException, IOException {
1289
1290 byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
1291 encodedKeyBuf.readBytes(encodedKey).release();
1292
1293 PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(
1294 keyPassword == null ? null : keyPassword.toCharArray(), encodedKey);
1295 try {
1296 return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
1297 } catch (InvalidKeySpecException ignore) {
1298 try {
1299 return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
1300 } catch (InvalidKeySpecException ignore2) {
1301 try {
1302 return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
1303 } catch (InvalidKeySpecException e) {
1304 throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
1305 }
1306 }
1307 }
1308 }
1309
1310 /**
1311 * Build a {@link TrustManagerFactory} from a certificate chain file.
1312 * @param certChainFile The certificate file to build from.
1313 * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
1314 * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
1315 */
1316 @Deprecated
1317 protected static TrustManagerFactory buildTrustManagerFactory(
1318 File certChainFile, TrustManagerFactory trustManagerFactory)
1319 throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1320 return buildTrustManagerFactory(certChainFile, trustManagerFactory, null);
1321 }
1322
1323 /**
1324 * Build a {@link TrustManagerFactory} from a certificate chain file.
1325 * @param certChainFile The certificate file to build from.
1326 * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
1327 * @param keyType The KeyStore Type you want to use
1328 * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
1329 */
1330 protected static TrustManagerFactory buildTrustManagerFactory(
1331 File certChainFile, TrustManagerFactory trustManagerFactory, String keyType)
1332 throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1333 X509Certificate[] x509Certs = toX509Certificates(certChainFile);
1334
1335 return buildTrustManagerFactory(x509Certs, trustManagerFactory, keyType);
1336 }
1337
1338 protected static X509Certificate[] toX509Certificates(File file) throws CertificateException {
1339 if (file == null) {
1340 return null;
1341 }
1342 return getCertificatesFromBuffers(PemReader.readCertificates(file));
1343 }
1344
1345 protected static X509Certificate[] toX509Certificates(InputStream in) throws CertificateException {
1346 if (in == null) {
1347 return null;
1348 }
1349 return getCertificatesFromBuffers(PemReader.readCertificates(in));
1350 }
1351
1352 private static X509Certificate[] getCertificatesFromBuffers(ByteBuf[] certs) throws CertificateException {
1353 CertificateFactory cf = CertificateFactory.getInstance("X.509");
1354 X509Certificate[] x509Certs = new X509Certificate[certs.length];
1355
1356 try {
1357 for (int i = 0; i < certs.length; i++) {
1358 try (InputStream is = new ByteBufInputStream(certs[i], false)) {
1359 x509Certs[i] = (X509Certificate) cf.generateCertificate(is);
1360 } catch (IOException e) {
1361 // This is not expected to happen, but re-throw in case it does.
1362 throw new RuntimeException(e);
1363 }
1364 }
1365 } finally {
1366 for (ByteBuf buf: certs) {
1367 buf.release();
1368 }
1369 }
1370 return x509Certs;
1371 }
1372
1373 protected static TrustManagerFactory buildTrustManagerFactory(
1374 X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory, String keyStoreType)
1375 throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
1376 if (keyStoreType == null) {
1377 keyStoreType = KeyStore.getDefaultType();
1378 }
1379 final KeyStore ks = KeyStore.getInstance(keyStoreType);
1380 ks.load(null, null);
1381
1382 int i = 1;
1383 for (X509Certificate cert: certCollection) {
1384 String alias = Integer.toString(i);
1385 ks.setCertificateEntry(alias, cert);
1386 i++;
1387 }
1388
1389 // Set up trust manager factory to use our key store.
1390 if (trustManagerFactory == null) {
1391 trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
1392 }
1393 trustManagerFactory.init(ks);
1394
1395 return trustManagerFactory;
1396 }
1397
1398 static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException {
1399 try {
1400 return toPrivateKey(keyFile, keyPassword);
1401 } catch (Exception e) {
1402 throw new SSLException(e);
1403 }
1404 }
1405
1406 static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException {
1407 try {
1408 return toX509Certificates(file);
1409 } catch (CertificateException e) {
1410 throw new SSLException(e);
1411 }
1412 }
1413
1414 protected static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile,
1415 String keyAlgorithm, PrivateKey key,
1416 String keyPassword, KeyManagerFactory kmf,
1417 String keyStore)
1418 throws KeyStoreException, NoSuchAlgorithmException, IOException,
1419 CertificateException, UnrecoverableKeyException {
1420 if (keyAlgorithm == null) {
1421 keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1422 }
1423 char[] keyPasswordChars = keyStorePassword(keyPassword);
1424 KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars, keyStore);
1425 return buildKeyManagerFactory(ks, keyAlgorithm, keyPasswordChars, kmf);
1426 }
1427
1428 static KeyManagerFactory buildKeyManagerFactory(KeyStore ks,
1429 String keyAlgorithm,
1430 char[] keyPasswordChars, KeyManagerFactory kmf)
1431 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
1432 // Set up key manager factory to use our key store
1433 if (kmf == null) {
1434 if (keyAlgorithm == null) {
1435 keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1436 }
1437 kmf = KeyManagerFactory.getInstance(keyAlgorithm);
1438 }
1439 kmf.init(ks, keyPasswordChars);
1440
1441 return kmf;
1442 }
1443
1444 static char[] keyStorePassword(String keyPassword) {
1445 return keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray();
1446 }
1447 }