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