View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
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 }