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.logging.InternalLogger;
20  import org.jboss.netty.logging.InternalLoggerFactory;
21  
22  import javax.net.ssl.SSLContext;
23  import javax.net.ssl.SSLEngine;
24  import javax.net.ssl.SSLSessionContext;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.List;
29  
30  /**
31   * An {@link SslContext} which uses JDK's SSL/TLS implementation.
32   */
33  public abstract class JdkSslContext extends SslContext {
34  
35      private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class);
36  
37      static final String PROTOCOL = "TLS";
38      static final String[] PROTOCOLS;
39      static final List<String> DEFAULT_CIPHERS;
40  
41      static {
42          SSLContext context;
43          try {
44              context = SSLContext.getInstance(PROTOCOL);
45              context.init(null, null, null);
46          } catch (Exception e) {
47              throw new Error("failed to initialize the default SSL context", e);
48          }
49  
50          SSLEngine engine = context.createSSLEngine();
51  
52          // Choose the sensible default list of protocols.
53          String[] supportedProtocols = engine.getSupportedProtocols();
54          List<String> protocols = new ArrayList<String>();
55          addIfSupported(
56                  supportedProtocols, protocols,
57                  "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
58  
59          if (!protocols.isEmpty()) {
60              PROTOCOLS = protocols.toArray(new String[protocols.size()]);
61          } else {
62              PROTOCOLS = engine.getEnabledProtocols();
63          }
64  
65          // Choose the sensible default list of cipher suites.
66          String[] supportedCiphers = engine.getSupportedCipherSuites();
67          List<String> ciphers = new ArrayList<String>();
68          addIfSupported(
69                  supportedCiphers, ciphers,
70                  // XXX: Make sure to sync this list with OpenSslEngineFactory.
71                  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // since JDK 8
72                  "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
73                  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
74                  "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
75                  "TLS_RSA_WITH_AES_128_GCM_SHA256", // since JDK 8
76                  "SSL_RSA_WITH_RC4_128_SHA",
77                  "SSL_RSA_WITH_RC4_128_MD5",
78                  "TLS_RSA_WITH_AES_128_CBC_SHA",
79                  "TLS_RSA_WITH_AES_256_CBC_SHA",
80                  "SSL_RSA_WITH_DES_CBC_SHA");
81  
82          if (!ciphers.isEmpty()) {
83              DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
84          } else {
85              // Use the default from JDK as fallback.
86              DEFAULT_CIPHERS = Collections.unmodifiableList(Arrays.asList(engine.getEnabledCipherSuites()));
87          }
88  
89          if (logger.isDebugEnabled()) {
90              logger.debug("Default protocols (JDK): " + PROTOCOLS);
91              logger.debug("Default cipher suites (JDK): " + DEFAULT_CIPHERS);
92          }
93      }
94  
95      private static void addIfSupported(String[] supported, List<String> enabled, String... names) {
96          for (String n: names) {
97              for (String s: supported) {
98                  if (n.equals(s)) {
99                      enabled.add(s);
100                     break;
101                 }
102             }
103         }
104     }
105 
106     private final String[] cipherSuites;
107     private final List<String> unmodifiableCipherSuites;
108 
109     JdkSslContext(SslBufferPool bufferPool, Iterable<String> ciphers) {
110         super(bufferPool);
111         cipherSuites = toCipherSuiteArray(ciphers);
112         unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
113     }
114 
115     /**
116      * Returns the JDK {@link SSLContext} object held by this context.
117      */
118     public abstract SSLContext context();
119 
120     /**
121      * Returns the JDK {@link SSLSessionContext} object held by this context.
122      */
123     public final SSLSessionContext sessionContext() {
124         if (isServer()) {
125             return context().getServerSessionContext();
126         } else {
127             return context().getClientSessionContext();
128         }
129     }
130 
131     @Override
132     public final List<String> cipherSuites() {
133         return unmodifiableCipherSuites;
134     }
135 
136     @Override
137     public final long sessionCacheSize() {
138         return sessionContext().getSessionCacheSize();
139     }
140 
141     @Override
142     public final long sessionTimeout() {
143         return sessionContext().getSessionTimeout();
144     }
145 
146     @Override
147     public final SSLEngine newEngine() {
148         SSLEngine engine = context().createSSLEngine();
149         engine.setEnabledCipherSuites(cipherSuites);
150         engine.setEnabledProtocols(PROTOCOLS);
151         engine.setUseClientMode(isClient());
152         return wrapEngine(engine);
153     }
154 
155     @Override
156     public final SSLEngine newEngine(String peerHost, int peerPort) {
157         SSLEngine engine = context().createSSLEngine(peerHost, peerPort);
158         engine.setEnabledCipherSuites(cipherSuites);
159         engine.setEnabledProtocols(PROTOCOLS);
160         engine.setUseClientMode(isClient());
161         return wrapEngine(engine);
162     }
163 
164     private SSLEngine wrapEngine(SSLEngine engine) {
165         if (nextProtocols().isEmpty()) {
166             return engine;
167         } else {
168             return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
169         }
170     }
171 
172     private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
173         if (ciphers == null) {
174             return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
175         } else {
176             List<String> newCiphers = new ArrayList<String>();
177             for (String c: ciphers) {
178                 if (c == null) {
179                     break;
180                 }
181                 newCiphers.add(c);
182             }
183             return newCiphers.toArray(new String[newCiphers.size()]);
184         }
185     }
186 }