1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl;
18
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.util.internal.EmptyArrays;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.logging.InternalLoggerFactory;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.security.InvalidAlgorithmParameterException;
27 import java.security.KeyException;
28 import java.security.KeyStore;
29 import java.security.KeyStoreException;
30 import java.security.NoSuchAlgorithmException;
31 import java.security.PrivateKey;
32 import java.security.Security;
33 import java.security.UnrecoverableKeyException;
34 import java.security.cert.CertificateException;
35 import java.security.cert.X509Certificate;
36 import java.security.spec.InvalidKeySpecException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43
44 import javax.crypto.NoSuchPaddingException;
45 import javax.net.ssl.KeyManagerFactory;
46 import javax.net.ssl.SSLContext;
47 import javax.net.ssl.SSLEngine;
48 import javax.net.ssl.SSLSessionContext;
49
50 import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
51 import static io.netty.handler.ssl.SslUtils.addIfSupported;
52 import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
53 import static io.netty.util.internal.ObjectUtil.checkNotNull;
54
55
56
57
58 public class JdkSslContext extends SslContext {
59
60 private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class);
61
62 static final String PROTOCOL = "TLS";
63 private static final String[] DEFAULT_PROTOCOLS;
64 private static final List<String> DEFAULT_CIPHERS;
65 private static final Set<String> SUPPORTED_CIPHERS;
66
67 static {
68 SSLContext context;
69 int i;
70 try {
71 context = SSLContext.getInstance(PROTOCOL);
72 context.init(null, null, null);
73 } catch (Exception e) {
74 throw new Error("failed to initialize the default SSL context", e);
75 }
76
77 SSLEngine engine = context.createSSLEngine();
78
79
80 final String[] supportedProtocols = engine.getSupportedProtocols();
81 Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length);
82 for (i = 0; i < supportedProtocols.length; ++i) {
83 supportedProtocolsSet.add(supportedProtocols[i]);
84 }
85 List<String> protocols = new ArrayList<String>();
86 addIfSupported(
87 supportedProtocolsSet, protocols,
88 "TLSv1.2", "TLSv1.1", "TLSv1");
89
90 if (!protocols.isEmpty()) {
91 DEFAULT_PROTOCOLS = protocols.toArray(new String[protocols.size()]);
92 } else {
93 DEFAULT_PROTOCOLS = engine.getEnabledProtocols();
94 }
95
96
97 final String[] supportedCiphers = engine.getSupportedCipherSuites();
98 SUPPORTED_CIPHERS = new HashSet<String>(supportedCiphers.length);
99 for (i = 0; i < supportedCiphers.length; ++i) {
100 String supportedCipher = supportedCiphers[i];
101 SUPPORTED_CIPHERS.add(supportedCipher);
102
103
104
105
106
107
108
109
110
111 if (supportedCipher.startsWith("SSL_")) {
112 final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length());
113 try {
114 engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName});
115 SUPPORTED_CIPHERS.add(tlsPrefixedCipherName);
116 } catch (IllegalArgumentException ignored) {
117
118 }
119 }
120 }
121 List<String> ciphers = new ArrayList<String>();
122 addIfSupported(SUPPORTED_CIPHERS, ciphers, DEFAULT_CIPHER_SUITES);
123 useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
124 DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
125
126 if (logger.isDebugEnabled()) {
127 logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
128 logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
129 }
130 }
131
132 private final String[] protocols;
133 private final String[] cipherSuites;
134 private final List<String> unmodifiableCipherSuites;
135 @SuppressWarnings("deprecation")
136 private final JdkApplicationProtocolNegotiator apn;
137 private final ClientAuth clientAuth;
138 private final SSLContext sslContext;
139 private final boolean isClient;
140
141
142
143
144
145
146
147
148 public JdkSslContext(SSLContext sslContext, boolean isClient,
149 ClientAuth clientAuth) {
150 this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
151 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
152 }
153
154
155
156
157
158
159
160
161
162
163
164 public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
165 CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
166 ClientAuth clientAuth) {
167 this(sslContext, isClient, ciphers, cipherFilter, toNegotiator(apn, !isClient), clientAuth, null, false);
168 }
169
170 @SuppressWarnings("deprecation")
171 JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
172 JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
173 super(startTls);
174 this.apn = checkNotNull(apn, "apn");
175 this.clientAuth = checkNotNull(clientAuth, "clientAuth");
176 cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
177 ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
178 this.protocols = protocols == null ? DEFAULT_PROTOCOLS : protocols;
179 unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
180 this.sslContext = checkNotNull(sslContext, "sslContext");
181 this.isClient = isClient;
182 }
183
184
185
186
187 public final SSLContext context() {
188 return sslContext;
189 }
190
191 @Override
192 public final boolean isClient() {
193 return isClient;
194 }
195
196
197
198
199 @Override
200 public final SSLSessionContext sessionContext() {
201 if (isServer()) {
202 return context().getServerSessionContext();
203 } else {
204 return context().getClientSessionContext();
205 }
206 }
207
208 @Override
209 public final List<String> cipherSuites() {
210 return unmodifiableCipherSuites;
211 }
212
213 @Override
214 public final long sessionCacheSize() {
215 return sessionContext().getSessionCacheSize();
216 }
217
218 @Override
219 public final long sessionTimeout() {
220 return sessionContext().getSessionTimeout();
221 }
222
223 @Override
224 public final SSLEngine newEngine(ByteBufAllocator alloc) {
225 return configureAndWrapEngine(context().createSSLEngine(), alloc);
226 }
227
228 @Override
229 public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
230 return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
231 }
232
233 @SuppressWarnings("deprecation")
234 private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
235 engine.setEnabledCipherSuites(cipherSuites);
236 engine.setEnabledProtocols(protocols);
237 engine.setUseClientMode(isClient());
238 if (isServer()) {
239 switch (clientAuth) {
240 case OPTIONAL:
241 engine.setWantClientAuth(true);
242 break;
243 case REQUIRE:
244 engine.setNeedClientAuth(true);
245 break;
246 case NONE:
247 break;
248 default:
249 throw new Error("Unknown auth " + clientAuth);
250 }
251 }
252 JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
253 if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
254 return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
255 .wrapSslEngine(engine, alloc, apn, isServer());
256 }
257 return factory.wrapSslEngine(engine, apn, isServer());
258 }
259
260 @Override
261 public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
262 return apn;
263 }
264
265
266
267
268
269
270
271 @SuppressWarnings("deprecation")
272 static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
273 if (config == null) {
274 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
275 }
276
277 switch(config.protocol()) {
278 case NONE:
279 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
280 case ALPN:
281 if (isServer) {
282 switch(config.selectorFailureBehavior()) {
283 case FATAL_ALERT:
284 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
285 case NO_ADVERTISE:
286 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
287 default:
288 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
289 .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
290 }
291 } else {
292 switch(config.selectedListenerFailureBehavior()) {
293 case ACCEPT:
294 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
295 case FATAL_ALERT:
296 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
297 default:
298 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
299 .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
300 }
301 }
302 case NPN:
303 if (isServer) {
304 switch(config.selectedListenerFailureBehavior()) {
305 case ACCEPT:
306 return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
307 case FATAL_ALERT:
308 return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
309 default:
310 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
311 .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
312 }
313 } else {
314 switch(config.selectorFailureBehavior()) {
315 case FATAL_ALERT:
316 return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
317 case NO_ADVERTISE:
318 return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
319 default:
320 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
321 .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
322 }
323 }
324 default:
325 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
326 .append(config.protocol()).append(" protocol").toString());
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339
340 @Deprecated
341 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
342 KeyManagerFactory kmf)
343 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
344 NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
345 CertificateException, KeyException, IOException {
346 String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
347 if (algorithm == null) {
348 algorithm = "SunX509";
349 }
350 return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf);
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 @Deprecated
368 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
369 String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf)
370 throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
371 InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
372 CertificateException, KeyException, UnrecoverableKeyException {
373 return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
374 toPrivateKey(keyFile, keyPassword), keyPassword, kmf);
375 }
376 }