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");
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                  // GCM (Galois/Counter Mode) requires JDK 8.
72                  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
73                  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
74                  // AES256 requires JCE unlimited strength jurisdiction policy files.
75                  "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
76                  // GCM (Galois/Counter Mode) requires JDK 8.
77                  "TLS_RSA_WITH_AES_128_GCM_SHA256",
78                  "TLS_RSA_WITH_AES_128_CBC_SHA",
79                  // AES256 requires JCE unlimited strength jurisdiction policy files.
80                  "TLS_RSA_WITH_AES_256_CBC_SHA",
81                  "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
82                  "SSL_RSA_WITH_RC4_128_SHA");
83  
84          if (!ciphers.isEmpty()) {
85              DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
86          } else {
87              // Use the default from JDK as fallback.
88              DEFAULT_CIPHERS = Collections.unmodifiableList(Arrays.asList(engine.getEnabledCipherSuites()));
89          }
90  
91          if (logger.isDebugEnabled()) {
92              logger.debug("Default protocols (JDK): " + Arrays.asList(PROTOCOLS));
93              logger.debug("Default cipher suites (JDK): " + DEFAULT_CIPHERS);
94          }
95      }
96  
97      private static void addIfSupported(String[] supported, List<String> enabled, String... names) {
98          for (String n: names) {
99              for (String s: supported) {
100                 if (n.equals(s)) {
101                     enabled.add(s);
102                     break;
103                 }
104             }
105         }
106     }
107 
108     private final String[] cipherSuites;
109     private final List<String> unmodifiableCipherSuites;
110 
111     JdkSslContext(SslBufferPool bufferPool, Iterable<String> ciphers) {
112         super(bufferPool);
113         cipherSuites = toCipherSuiteArray(ciphers);
114         unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
115     }
116 
117     /**
118      * Returns the JDK {@link SSLContext} object held by this context.
119      */
120     public abstract SSLContext context();
121 
122     /**
123      * Returns the JDK {@link SSLSessionContext} object held by this context.
124      */
125     public final SSLSessionContext sessionContext() {
126         if (isServer()) {
127             return context().getServerSessionContext();
128         } else {
129             return context().getClientSessionContext();
130         }
131     }
132 
133     @Override
134     public final List<String> cipherSuites() {
135         return unmodifiableCipherSuites;
136     }
137 
138     @Override
139     public final long sessionCacheSize() {
140         return sessionContext().getSessionCacheSize();
141     }
142 
143     @Override
144     public final long sessionTimeout() {
145         return sessionContext().getSessionTimeout();
146     }
147 
148     @Override
149     public final SSLEngine newEngine() {
150         SSLEngine engine = context().createSSLEngine();
151         engine.setEnabledCipherSuites(cipherSuites);
152         engine.setEnabledProtocols(PROTOCOLS);
153         engine.setUseClientMode(isClient());
154         return wrapEngine(engine);
155     }
156 
157     @Override
158     public final SSLEngine newEngine(String peerHost, int peerPort) {
159         SSLEngine engine = context().createSSLEngine(peerHost, peerPort);
160         engine.setEnabledCipherSuites(cipherSuites);
161         engine.setEnabledProtocols(PROTOCOLS);
162         engine.setUseClientMode(isClient());
163         return wrapEngine(engine);
164     }
165 
166     private SSLEngine wrapEngine(SSLEngine engine) {
167         if (nextProtocols().isEmpty()) {
168             return engine;
169         } else {
170             return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
171         }
172     }
173 
174     private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
175         if (ciphers == null) {
176             return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
177         } else {
178             List<String> newCiphers = new ArrayList<String>();
179             for (String c: ciphers) {
180                 if (c == null) {
181                     break;
182                 }
183                 newCiphers.add(c);
184             }
185             return newCiphers.toArray(new String[newCiphers.size()]);
186         }
187     }
188 }