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    *   http://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 org.jboss.netty.handler.ssl;
18  
19  import org.jboss.netty.channel.ChannelPipeline;
20  import org.jboss.netty.channel.ChannelPipelineFactory;
21  import org.jboss.netty.channel.Channels;
22  
23  import javax.net.ssl.SSLContext;
24  import javax.net.ssl.SSLEngine;
25  import javax.net.ssl.SSLException;
26  import javax.net.ssl.TrustManager;
27  import javax.net.ssl.TrustManagerFactory;
28  import java.io.File;
29  import java.util.List;
30  
31  /**
32   * A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
33   * Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
34   *
35   * <h3>Making your server support SSL/TLS</h3>
36   * <pre>
37   * // In your {@link ChannelPipelineFactory}:
38   * {@link ChannelPipeline} p = {@link Channels#pipeline()};
39   * {@link SslContext} sslCtx = {@link #newServerContext(File, File) SslContext.newServerContext(...)};
40   * p.addLast("ssl", {@link #newEngine() sslCtx.newEngine()});
41   * ...
42   * </pre>
43   *
44   * <h3>Making your client support SSL/TLS</h3>
45   * <pre>
46   * // In your {@link ChannelPipelineFactory}:
47   * {@link ChannelPipeline} p = {@link Channels#pipeline()};
48   * {@link SslContext} sslCtx = {@link #newClientContext(File) SslContext.newClientContext(...)};
49   * p.addLast("ssl", {@link #newEngine(String, int) sslCtx.newEngine(host, port)});
50   * ...
51   * </pre>
52   */
53  public abstract class SslContext {
54  
55      /**
56       * Returns the default server-side implementation provider currently in use.
57       *
58       * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
59       */
60      public static SslProvider defaultServerProvider() {
61          if (OpenSsl.isAvailable()) {
62              return SslProvider.OPENSSL;
63          } else {
64              return SslProvider.JDK;
65          }
66      }
67  
68      /**
69       * Returns the default client-side implementation provider currently in use.
70       *
71       * @return {@link SslProvider#JDK}, because it is the only implementation at the moment
72       */
73      public static SslProvider defaultClientProvider() {
74          return SslProvider.JDK;
75      }
76  
77      /**
78       * Creates a new server-side {@link SslContext}.
79       *
80       * @param certChainFile an X.509 certificate chain file in PEM format
81       * @param keyFile a PKCS#8 private key file in PEM format
82       * @return a new server-side {@link SslContext}
83       */
84      public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
85          return newServerContext(null, null, certChainFile, keyFile, null, null, null, 0, 0);
86      }
87  
88      /**
89       * Creates a new server-side {@link SslContext}.
90       *
91       * @param certChainFile an X.509 certificate chain file in PEM format
92       * @param keyFile a PKCS#8 private key file in PEM format
93       * @param keyPassword the password of the {@code keyFile}.
94       *                    {@code null} if it's not password-protected.
95       * @return a new server-side {@link SslContext}
96       */
97      public static SslContext newServerContext(
98              File certChainFile, File keyFile, String keyPassword) throws SSLException {
99          return newServerContext(null, null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
100     }
101 
102     /**
103      * Creates a new server-side {@link SslContext}.
104      *
105      * @param bufPool the buffer pool which will be used by the returned {@link SslContext}.
106      *                {@code null} to use the default buffer pool.
107      * @param certChainFile an X.509 certificate chain file in PEM format
108      * @param keyFile a PKCS#8 private key file in PEM format
109      * @param keyPassword the password of the {@code keyFile}.
110      *                    {@code null} if it's not password-protected.
111      * @param ciphers the cipher suites to enable, in the order of preference.
112      *                {@code null} to use the default cipher suites.
113      * @param nextProtocols the application layer protocols to accept, in the order of preference.
114      *                      {@code null} to disable TLS NPN/ALPN extension.
115      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
116      *                         {@code 0} to use the default value.
117      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
118      *                       {@code 0} to use the default value.
119      * @return a new server-side {@link SslContext}
120      */
121     public static SslContext newServerContext(
122             SslBufferPool bufPool,
123             File certChainFile, File keyFile, String keyPassword,
124             Iterable<String> ciphers, Iterable<String> nextProtocols,
125             long sessionCacheSize, long sessionTimeout) throws SSLException {
126         return newServerContext(
127                 null, bufPool, certChainFile, keyFile, keyPassword,
128                 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
129     }
130 
131     /**
132      * Creates a new server-side {@link SslContext}.
133      *
134      * @param provider the {@link SslContext} implementation to use.
135      *                 {@code null} to use the current default one.
136      * @param certChainFile an X.509 certificate chain file in PEM format
137      * @param keyFile a PKCS#8 private key file in PEM format
138      * @return a new server-side {@link SslContext}
139      */
140     public static SslContext newServerContext(
141             SslProvider provider, File certChainFile, File keyFile) throws SSLException {
142         return newServerContext(provider, null, certChainFile, keyFile, null, null, null, 0, 0);
143     }
144 
145     /**
146      * Creates a new server-side {@link SslContext}.
147      *
148      * @param provider the {@link SslContext} implementation to use.
149      *                 {@code null} to use the current default one.
150      * @param certChainFile an X.509 certificate chain file in PEM format
151      * @param keyFile a PKCS#8 private key file in PEM format
152      * @param keyPassword the password of the {@code keyFile}.
153      *                    {@code null} if it's not password-protected.
154      * @return a new server-side {@link SslContext}
155      */
156     public static SslContext newServerContext(
157             SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
158         return newServerContext(provider, null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
159     }
160 
161     /**
162      * Creates a new server-side {@link SslContext}.
163      *
164      * @param provider the {@link SslContext} implementation to use.
165      *                 {@code null} to use the current default one.
166      * @param bufPool the buffer pool which will be used by the returned {@link SslContext}.
167      *                {@code null} to use the default buffer pool.
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      */
182     public static SslContext newServerContext(
183             SslProvider provider, SslBufferPool bufPool,
184             File certChainFile, File keyFile, String keyPassword,
185             Iterable<String> ciphers, Iterable<String> nextProtocols,
186             long sessionCacheSize, long sessionTimeout) throws SSLException {
187 
188         if (provider == null) {
189             provider = OpenSsl.isAvailable()? SslProvider.OPENSSL : SslProvider.JDK;
190         }
191 
192         switch (provider) {
193             case JDK:
194                 return new JdkSslServerContext(
195                         bufPool, certChainFile, keyFile, keyPassword,
196                         ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
197             case OPENSSL:
198                 return new OpenSslServerContext(
199                         bufPool, certChainFile, keyFile, keyPassword,
200                         ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
201             default:
202                 throw new Error(provider.toString());
203         }
204     }
205 
206     /**
207      * Creates a new client-side {@link SslContext}.
208      *
209      * @return a new client-side {@link SslContext}
210      */
211     public static SslContext newClientContext() throws SSLException {
212         return newClientContext(null, null, null, null, null, null, 0, 0);
213     }
214 
215     /**
216      * Creates a new client-side {@link SslContext}.
217      *
218      * @param certChainFile an X.509 certificate chain file in PEM format
219      *
220      * @return a new client-side {@link SslContext}
221      */
222     public static SslContext newClientContext(File certChainFile) throws SSLException {
223         return newClientContext(null, null, certChainFile, null, null, null, 0, 0);
224     }
225 
226     /**
227      * Creates a new client-side {@link SslContext}.
228      *
229      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
230      *                            that verifies the certificates sent from servers.
231      *                            {@code null} to use the default.
232      *
233      * @return a new client-side {@link SslContext}
234      */
235     public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
236         return newClientContext(null, null, null, trustManagerFactory, null, null, 0, 0);
237     }
238 
239     /**
240      * Creates a new client-side {@link SslContext}.
241      *
242      * @param certChainFile an X.509 certificate chain file in PEM format.
243      *                      {@code null} to use the system default
244      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
245      *                            that verifies the certificates sent from servers.
246      *                            {@code null} to use the default.
247      *
248      * @return a new client-side {@link SslContext}
249      */
250     public static SslContext newClientContext(
251             File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
252         return newClientContext(null, null, certChainFile, trustManagerFactory, null, null, 0, 0);
253     }
254 
255     /**
256      * Creates a new client-side {@link SslContext}.
257      *
258      * @param bufPool the buffer pool which will be used by the returned {@link SslContext}.
259      *                {@code null} to use the default buffer pool.
260      * @param certChainFile an X.509 certificate chain file in PEM format.
261      *                      {@code null} to use the system default
262      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
263      *                            that verifies the certificates sent from servers.
264      *                            {@code null} to use the default.
265      * @param ciphers the cipher suites to enable, in the order of preference.
266      *                {@code null} to use the default cipher suites.
267      * @param nextProtocols the application layer protocols to accept, in the order of preference.
268      *                      {@code null} to disable TLS NPN/ALPN extension.
269      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
270      *                         {@code 0} to use the default value.
271      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
272      *                       {@code 0} to use the default value.
273      *
274      * @return a new client-side {@link SslContext}
275      */
276     public static SslContext newClientContext(
277             SslBufferPool bufPool,
278             File certChainFile, TrustManagerFactory trustManagerFactory,
279             Iterable<String> ciphers, Iterable<String> nextProtocols,
280             long sessionCacheSize, long sessionTimeout) throws SSLException {
281         return newClientContext(
282                 null, bufPool, certChainFile, trustManagerFactory,
283                 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
284     }
285 
286     /**
287      * Creates a new client-side {@link SslContext}.
288      *
289      * @param provider the {@link SslContext} implementation to use.
290      *                 {@code null} to use the current default one.
291      *
292      * @return a new client-side {@link SslContext}
293      */
294     public static SslContext newClientContext(SslProvider provider) throws SSLException {
295         return newClientContext(provider, null, null, null, null, null, 0, 0);
296     }
297 
298     /**
299      * Creates a new client-side {@link SslContext}.
300      *
301      * @param provider the {@link SslContext} implementation to use.
302      *                 {@code null} to use the current default one.
303      * @param certChainFile an X.509 certificate chain file in PEM format.
304      *                      {@code null} to use the system default
305      *
306      * @return a new client-side {@link SslContext}
307      */
308     public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
309         return newClientContext(provider, null, certChainFile, null, null, null, 0, 0);
310     }
311 
312     /**
313      * Creates a new client-side {@link SslContext}.
314      *
315      * @param provider the {@link SslContext} implementation to use.
316      *                 {@code null} to use the current default one.
317      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
318      *                            that verifies the certificates sent from servers.
319      *                            {@code null} to use the default.
320      *
321      * @return a new client-side {@link SslContext}
322      */
323     public static SslContext newClientContext(
324             SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
325         return newClientContext(provider, null, null, trustManagerFactory, null, null, 0, 0);
326     }
327 
328     /**
329      * Creates a new client-side {@link SslContext}.
330      *
331      * @param provider the {@link SslContext} implementation to use.
332      *                 {@code null} to use the current default one.
333      * @param certChainFile an X.509 certificate chain file in PEM format.
334      *                      {@code null} to use the system default
335      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
336      *                            that verifies the certificates sent from servers.
337      *                            {@code null} to use the default.
338      *
339      * @return a new client-side {@link SslContext}
340      */
341     public static SslContext newClientContext(
342             SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
343         return newClientContext(provider, null, certChainFile, trustManagerFactory, null, null, 0, 0);
344     }
345 
346     /**
347      * Creates a new client-side {@link SslContext}.
348      *
349      * @param provider the {@link SslContext} implementation to use.
350      *                 {@code null} to use the current default one.
351      * @param bufPool the buffer pool which will be used by the returned {@link SslContext}.
352      *                {@code null} to use the default buffer pool.
353      * @param certChainFile an X.509 certificate chain file in PEM format.
354      *                      {@code null} to use the system default
355      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
356      *                            that verifies the certificates sent from servers.
357      *                            {@code null} to use the default.
358      * @param ciphers the cipher suites to enable, in the order of preference.
359      *                {@code null} to use the default cipher suites.
360      * @param nextProtocols the application layer protocols to accept, in the order of preference.
361      *                      {@code null} to disable TLS NPN/ALPN extension.
362      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
363      *                         {@code 0} to use the default value.
364      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
365      *                       {@code 0} to use the default value.
366      *
367      * @return a new client-side {@link SslContext}
368      */
369     public static SslContext newClientContext(
370             SslProvider provider, SslBufferPool bufPool,
371             File certChainFile, TrustManagerFactory trustManagerFactory,
372             Iterable<String> ciphers, Iterable<String> nextProtocols,
373             long sessionCacheSize, long sessionTimeout) throws SSLException {
374 
375         if (provider != null && provider != SslProvider.JDK) {
376             throw new SSLException("client context unsupported for: " + provider);
377         }
378 
379         return new JdkSslClientContext(
380                 bufPool, certChainFile, trustManagerFactory,
381                 ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
382     }
383 
384     private final SslBufferPool bufferPool;
385 
386     SslContext(SslBufferPool bufferPool) {
387         this.bufferPool = bufferPool == null? newBufferPool() : bufferPool;
388     }
389 
390     SslBufferPool newBufferPool() {
391         return new SslBufferPool(false, false);
392     }
393 
394     /**
395      * Returns {@code true} if and only if this context is for server-side.
396      */
397     public final boolean isServer() {
398         return !isClient();
399     }
400 
401     /**
402      * Returns the {@link SslBufferPool} used by the {@link SSLEngine} and {@link SslHandler} created by this context.
403      */
404     public final SslBufferPool bufferPool() {
405         return bufferPool;
406     }
407 
408     /**
409      * Returns the {@code true} if and only if this context is for client-side.
410      */
411     public abstract boolean isClient();
412 
413     /**
414      * Returns the list of enabled cipher suites, in the order of preference.
415      */
416     public abstract List<String> cipherSuites();
417 
418     /**
419      * Returns the size of the cache used for storing SSL session objects.
420      */
421     public abstract long sessionCacheSize();
422 
423     /**
424      * Returns the timeout for the cached SSL session objects, in seconds.
425      */
426     public abstract long sessionTimeout();
427 
428     /**
429      * Returns the list of application layer protocols for the TLS NPN/ALPN extension, in the order of preference.
430      *
431      * @return the list of application layer protocols.
432      *         {@code null} if NPN/ALPN extension has been disabled.
433      */
434     public abstract List<String> nextProtocols();
435 
436     /**
437      * Creates a new {@link SSLEngine}.
438      *
439      * @return a new {@link SSLEngine}
440      */
441     public abstract SSLEngine newEngine();
442 
443     /**
444      * Creates a new {@link SSLEngine} using advisory peer information.
445      *
446      * @param peerHost the non-authoritative name of the host
447      * @param peerPort the non-authoritative port
448      *
449      * @return a new {@link SSLEngine}
450      */
451     public abstract SSLEngine newEngine(String peerHost, int peerPort);
452 
453     /**
454      * Creates a new {@link SslHandler}.
455      *
456      * @return a new {@link SslHandler}
457      */
458     public final SslHandler newHandler() {
459         return newHandler(newEngine());
460     }
461 
462     /**
463      * Creates a new {@link SslHandler} with advisory peer information.
464      *
465      * @param peerHost the non-authoritative name of the host
466      * @param peerPort the non-authoritative port
467      *
468      * @return a new {@link SslHandler}
469      */
470     public final SslHandler newHandler(String peerHost, int peerPort) {
471         return newHandler(newEngine(peerHost, peerPort));
472     }
473 
474     private SslHandler newHandler(SSLEngine engine) {
475         SslHandler handler = new SslHandler(engine, bufferPool());
476         if (isClient()) {
477             handler.setIssueHandshake(true);
478         }
479         handler.setCloseOnSSLException(true);
480         return handler;
481     }
482 }