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.util.CharsetUtil;
20 import io.netty.util.internal.PlatformDependent;
21 import io.netty.util.internal.SuppressJava6Requirement;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.security.InvalidAlgorithmParameterException;
26 import java.security.KeyException;
27 import java.security.KeyStore;
28 import java.security.KeyStoreException;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.Provider;
31 import javax.crypto.NoSuchPaddingException;
32 import javax.net.ssl.KeyManager;
33
34 import javax.net.ssl.KeyManagerFactory;
35 import javax.net.ssl.SSLContext;
36 import javax.net.ssl.SSLException;
37 import javax.net.ssl.SSLSessionContext;
38 import javax.net.ssl.TrustManager;
39 import javax.net.ssl.TrustManagerFactory;
40 import javax.net.ssl.X509ExtendedTrustManager;
41 import java.io.File;
42 import java.security.PrivateKey;
43 import java.security.UnrecoverableKeyException;
44 import java.security.cert.CertificateException;
45 import java.security.cert.X509Certificate;
46 import java.security.spec.InvalidKeySpecException;
47
48 import static io.netty.handler.ssl.SslUtils.PROBING_CERT;
49 import static io.netty.handler.ssl.SslUtils.PROBING_KEY;
50
51 /**
52 * A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
53 *
54 * @deprecated Use {@link SslContextBuilder} to create {@link JdkSslContext} instances and only
55 * use {@link JdkSslContext} in your code.
56 */
57 @Deprecated
58 public final class JdkSslServerContext extends JdkSslContext {
59
60 private static final boolean WRAP_TRUST_MANAGER;
61 static {
62 boolean wrapTrustManager = false;
63 if (PlatformDependent.javaVersion() >= 7) {
64 try {
65 checkIfWrappingTrustManagerIsSupported();
66 wrapTrustManager = true;
67 } catch (Throwable ignore) {
68 // Just don't wrap as we might not be able to do so because of FIPS:
69 // See https://github.com/netty/netty/issues/13840
70 }
71 }
72 WRAP_TRUST_MANAGER = wrapTrustManager;
73 }
74
75 // Package-private for testing.
76 @SuppressJava6Requirement(reason = "Guarded by java version check")
77 static void checkIfWrappingTrustManagerIsSupported() throws CertificateException,
78 InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException,
79 InvalidKeySpecException, IOException, KeyException, KeyStoreException, UnrecoverableKeyException {
80 X509Certificate[] certs = toX509Certificates(
81 new ByteArrayInputStream(PROBING_CERT.getBytes(CharsetUtil.US_ASCII)));
82 PrivateKey privateKey = toPrivateKey(new ByteArrayInputStream(
83 PROBING_KEY.getBytes(CharsetUtil.UTF_8)), null);
84 char[] keyStorePassword = keyStorePassword(null);
85 KeyStore ks = buildKeyStore(certs, privateKey, keyStorePassword, null);
86 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
87 kmf.init(ks, keyStorePassword);
88
89 SSLContext ctx = SSLContext.getInstance(PROTOCOL);
90 TrustManagerFactory tm = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
91 tm.init((KeyStore) null);
92 TrustManager[] managers = tm.getTrustManagers();
93
94 ctx.init(kmf.getKeyManagers(), wrapTrustManagerIfNeeded(managers), null);
95 }
96
97 /**
98 * Creates a new instance.
99 *
100 * @param certChainFile an X.509 certificate chain file in PEM format
101 * @param keyFile a PKCS#8 private key file in PEM format
102 * @deprecated use {@link SslContextBuilder}
103 */
104 @Deprecated
105 public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException {
106 this(null, certChainFile, keyFile, null, null, IdentityCipherSuiteFilter.INSTANCE,
107 JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0, null);
108 }
109
110 /**
111 * Creates a new instance.
112 *
113 * @param certChainFile an X.509 certificate chain file in PEM format
114 * @param keyFile a PKCS#8 private key file in PEM format
115 * @param keyPassword the password of the {@code keyFile}.
116 * {@code null} if it's not password-protected.
117 * @deprecated use {@link SslContextBuilder}
118 */
119 @Deprecated
120 public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
121 this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
122 JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
123 }
124
125 /**
126 * Creates a new instance.
127 *
128 * @param certChainFile an X.509 certificate chain file in PEM format
129 * @param keyFile a PKCS#8 private key file in PEM format
130 * @param keyPassword the password of the {@code keyFile}.
131 * {@code null} if it's not password-protected.
132 * @param ciphers the cipher suites to enable, in the order of preference.
133 * {@code null} to use the default cipher suites.
134 * @param nextProtocols the application layer protocols to accept, in the order of preference.
135 * {@code null} to disable TLS NPN/ALPN extension.
136 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
137 * {@code 0} to use the default value.
138 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
139 * {@code 0} to use the default value.
140 * @deprecated use {@link SslContextBuilder}
141 */
142 @Deprecated
143 public JdkSslServerContext(
144 File certChainFile, File keyFile, String keyPassword,
145 Iterable<String> ciphers, Iterable<String> nextProtocols,
146 long sessionCacheSize, long sessionTimeout) throws SSLException {
147 this(null, certChainFile, keyFile, keyPassword, ciphers, IdentityCipherSuiteFilter.INSTANCE,
148 toNegotiator(toApplicationProtocolConfig(nextProtocols), true), sessionCacheSize,
149 sessionTimeout, KeyStore.getDefaultType());
150 }
151
152 /**
153 * Creates a new instance.
154 *
155 * @param certChainFile an X.509 certificate chain file in PEM format
156 * @param keyFile a PKCS#8 private key file in PEM format
157 * @param keyPassword the password of the {@code keyFile}.
158 * {@code null} if it's not password-protected.
159 * @param ciphers the cipher suites to enable, in the order of preference.
160 * {@code null} to use the default cipher suites.
161 * @param cipherFilter a filter to apply over the supplied list of ciphers
162 * @param apn Provides a means to configure parameters related to application protocol negotiation.
163 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
164 * {@code 0} to use the default value.
165 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
166 * {@code 0} to use the default value.
167 * @deprecated use {@link SslContextBuilder}
168 */
169 @Deprecated
170 public JdkSslServerContext(
171 File certChainFile, File keyFile, String keyPassword,
172 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
173 long sessionCacheSize, long sessionTimeout) throws SSLException {
174 this(null, certChainFile, keyFile, keyPassword, ciphers, cipherFilter,
175 toNegotiator(apn, true), sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
176 }
177
178 /**
179 * Creates a new instance.
180 *
181 * @param certChainFile an X.509 certificate chain file in PEM format
182 * @param keyFile a PKCS#8 private key file in PEM format
183 * @param keyPassword the password of the {@code keyFile}.
184 * {@code null} if it's not password-protected.
185 * @param ciphers the cipher suites to enable, in the order of preference.
186 * {@code null} to use the default cipher suites.
187 * @param cipherFilter a filter to apply over the supplied list of ciphers
188 * @param apn Application Protocol Negotiator object.
189 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
190 * {@code 0} to use the default value.
191 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
192 * {@code 0} to use the default value.
193 * @deprecated use {@link SslContextBuilder}
194 */
195 @Deprecated
196 public JdkSslServerContext(
197 File certChainFile, File keyFile, String keyPassword,
198 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
199 long sessionCacheSize, long sessionTimeout) throws SSLException {
200 this(null, certChainFile, keyFile, keyPassword, ciphers, cipherFilter, apn,
201 sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
202 }
203
204 JdkSslServerContext(Provider provider,
205 File certChainFile, File keyFile, String keyPassword,
206 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
207 long sessionCacheSize, long sessionTimeout, String keyStore) throws SSLException {
208 super(newSSLContext(provider, null, null,
209 toX509CertificatesInternal(certChainFile), toPrivateKeyInternal(keyFile, keyPassword),
210 keyPassword, null, sessionCacheSize, sessionTimeout, keyStore), false,
211 ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
212 }
213
214 /**
215 * Creates a new instance.
216 * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
217 * This provides the certificate collection used for mutual authentication.
218 * {@code null} to use the system default
219 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
220 * that verifies the certificates sent from clients.
221 * {@code null} to use the default or the results of parsing
222 * {@code trustCertCollectionFile}.
223 * @param keyCertChainFile an X.509 certificate chain file in PEM format
224 * @param keyFile a PKCS#8 private key file in PEM format
225 * @param keyPassword the password of the {@code keyFile}.
226 * {@code null} if it's not password-protected.
227 * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
228 * that is used to encrypt data being sent to clients.
229 * {@code null} to use the default or the results of parsing
230 * {@code keyCertChainFile} and {@code keyFile}.
231 * @param ciphers the cipher suites to enable, in the order of preference.
232 * {@code null} to use the default cipher suites.
233 * @param cipherFilter a filter to apply over the supplied list of ciphers
234 * Only required if {@code provider} is {@link SslProvider#JDK}
235 * @param apn Provides a means to configure parameters related to application protocol negotiation.
236 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
237 * {@code 0} to use the default value.
238 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
239 * {@code 0} to use the default value.
240 * @deprecated use {@link SslContextBuilder}
241 */
242 @Deprecated
243 public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
244 File keyCertChainFile, File keyFile, String keyPassword,
245 KeyManagerFactory keyManagerFactory,
246 Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
247 long sessionCacheSize, long sessionTimeout) throws SSLException {
248 super(newSSLContext(null, toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
249 toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
250 keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, null), false,
251 ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
252 }
253
254 /**
255 * Creates a new instance.
256 * @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
257 * This provides the certificate collection used for mutual authentication.
258 * {@code null} to use the system default
259 * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
260 * that verifies the certificates sent from clients.
261 * {@code null} to use the default or the results of parsing
262 * {@code trustCertCollectionFile}
263 * @param keyCertChainFile an X.509 certificate chain file in PEM format
264 * @param keyFile a PKCS#8 private key file in PEM format
265 * @param keyPassword the password of the {@code keyFile}.
266 * {@code null} if it's not password-protected.
267 * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
268 * that is used to encrypt data being sent to clients.
269 * {@code null} to use the default or the results of parsing
270 * {@code keyCertChainFile} and {@code keyFile}.
271 * @param ciphers the cipher suites to enable, in the order of preference.
272 * {@code null} to use the default cipher suites.
273 * @param cipherFilter a filter to apply over the supplied list of ciphers
274 * Only required if {@code provider} is {@link SslProvider#JDK}
275 * @param apn Application Protocol Negotiator object.
276 * @param sessionCacheSize the size of the cache used for storing SSL session objects.
277 * {@code 0} to use the default value.
278 * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
279 * {@code 0} to use the default value
280 * @deprecated use {@link SslContextBuilder}
281 */
282 @Deprecated
283 public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
284 File keyCertChainFile, File keyFile, String keyPassword,
285 KeyManagerFactory keyManagerFactory,
286 Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
287 JdkApplicationProtocolNegotiator apn,
288 long sessionCacheSize, long sessionTimeout) throws SSLException {
289 super(newSSLContext(null, toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
290 toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
291 keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), false,
292 ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
293 }
294
295 JdkSslServerContext(Provider provider,
296 X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
297 X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
298 KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
299 ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
300 ClientAuth clientAuth, String[] protocols, boolean startTls,
301 String keyStore) throws SSLException {
302 super(newSSLContext(provider, trustCertCollection, trustManagerFactory, keyCertChain, key,
303 keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore), false,
304 ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, protocols, startTls);
305 }
306
307 private static SSLContext newSSLContext(Provider sslContextProvider, X509Certificate[] trustCertCollection,
308 TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
309 PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
310 long sessionCacheSize, long sessionTimeout, String keyStore)
311 throws SSLException {
312 if (key == null && keyManagerFactory == null) {
313 throw new NullPointerException("key, keyManagerFactory");
314 }
315
316 try {
317 if (trustCertCollection != null) {
318 trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
319 } else if (trustManagerFactory == null) {
320 // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
321 trustManagerFactory = TrustManagerFactory.getInstance(
322 TrustManagerFactory.getDefaultAlgorithm());
323 trustManagerFactory.init((KeyStore) null);
324 }
325
326 if (key != null) {
327 keyManagerFactory = buildKeyManagerFactory(keyCertChain, null,
328 key, keyPassword, keyManagerFactory, null);
329 }
330
331 // Initialize the SSLContext to work with our key managers.
332 SSLContext ctx = sslContextProvider == null ? SSLContext.getInstance(PROTOCOL)
333 : SSLContext.getInstance(PROTOCOL, sslContextProvider);
334 ctx.init(keyManagerFactory.getKeyManagers(),
335 wrapTrustManagerIfNeeded(trustManagerFactory.getTrustManagers()),
336 null);
337
338 SSLSessionContext sessCtx = ctx.getServerSessionContext();
339 if (sessionCacheSize > 0) {
340 sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
341 }
342 if (sessionTimeout > 0) {
343 sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
344 }
345 return ctx;
346 } catch (Exception e) {
347 if (e instanceof SSLException) {
348 throw (SSLException) e;
349 }
350 throw new SSLException("failed to initialize the server-side SSL context", e);
351 }
352 }
353
354 @SuppressJava6Requirement(reason = "Guarded by java version check")
355 private static TrustManager[] wrapTrustManagerIfNeeded(TrustManager[] trustManagers) {
356 if (WRAP_TRUST_MANAGER && PlatformDependent.javaVersion() >= 7) {
357 for (int i = 0; i < trustManagers.length; i++) {
358 TrustManager tm = trustManagers[i];
359 if (tm instanceof X509ExtendedTrustManager) {
360 // Wrap the TrustManager to provide a better exception message for users to debug hostname
361 // validation failures.
362 trustManagers[i] = new EnhancingX509ExtendedTrustManager((X509ExtendedTrustManager) tm);
363 }
364 }
365 }
366 return trustManagers;
367 }
368 }