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.ReferenceCountUtil;
21 import io.netty.util.internal.EmptyArrays;
22 import io.netty.util.internal.logging.InternalLogger;
23 import io.netty.util.internal.logging.InternalLoggerFactory;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.security.InvalidAlgorithmParameterException;
28 import java.security.KeyException;
29 import java.security.KeyStore;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.Provider;
33 import java.security.Security;
34 import java.security.UnrecoverableKeyException;
35 import java.security.cert.CertificateException;
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.LinkedHashSet;
42 import java.util.List;
43 import java.util.Set;
44 import javax.crypto.NoSuchPaddingException;
45 import javax.net.ssl.KeyManagerFactory;
46 import javax.net.ssl.SNIServerName;
47 import javax.net.ssl.SSLContext;
48 import javax.net.ssl.SSLEngine;
49 import javax.net.ssl.SSLParameters;
50 import javax.net.ssl.SSLSessionContext;
51
52 import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
53 import static io.netty.handler.ssl.SslUtils.addIfSupported;
54 import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
55 import static io.netty.util.internal.ObjectUtil.checkNotNull;
56
57
58
59
60 public class JdkSslContext extends SslContext {
61
62 private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class);
63
64 static final String PROTOCOL = "TLS";
65 private static final String[] DEFAULT_PROTOCOLS;
66 private static final List<String> DEFAULT_CIPHERS;
67 private static final List<String> DEFAULT_CIPHERS_NON_TLSV13;
68 private static final Set<String> SUPPORTED_CIPHERS;
69 private static final Set<String> SUPPORTED_CIPHERS_NON_TLSV13;
70 private static final Provider DEFAULT_PROVIDER;
71
72 static {
73 Defaults defaults = new Defaults();
74 defaults.init();
75
76 DEFAULT_PROVIDER = defaults.defaultProvider;
77 DEFAULT_PROTOCOLS = defaults.defaultProtocols;
78 SUPPORTED_CIPHERS = defaults.supportedCiphers;
79 DEFAULT_CIPHERS = defaults.defaultCiphers;
80 DEFAULT_CIPHERS_NON_TLSV13 = defaults.defaultCiphersNonTLSv13;
81 SUPPORTED_CIPHERS_NON_TLSV13 = defaults.supportedCiphersNonTLSv13;
82
83 if (logger.isDebugEnabled()) {
84 logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
85 logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
86 }
87 }
88
89 private static final class Defaults {
90 String[] defaultProtocols;
91 List<String> defaultCiphers;
92 List<String> defaultCiphersNonTLSv13;
93 Set<String> supportedCiphers;
94 Set<String> supportedCiphersNonTLSv13;
95 Provider defaultProvider;
96
97 void init() {
98 SSLContext context;
99 try {
100 context = SSLContext.getInstance(PROTOCOL);
101 context.init(null, null, null);
102 } catch (Exception e) {
103 throw new Error("failed to initialize the default SSL context", e);
104 }
105
106 defaultProvider = context.getProvider();
107
108 SSLEngine engine = context.createSSLEngine();
109 defaultProtocols = defaultProtocols(context, engine);
110
111 supportedCiphers = Collections.unmodifiableSet(supportedCiphers(engine));
112 defaultCiphers = Collections.unmodifiableList(defaultCiphers(engine, supportedCiphers));
113
114 List<String> ciphersNonTLSv13 = new ArrayList<String>(defaultCiphers);
115 ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
116 defaultCiphersNonTLSv13 = Collections.unmodifiableList(ciphersNonTLSv13);
117
118 Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(supportedCiphers);
119 suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
120 supportedCiphersNonTLSv13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13);
121 }
122 }
123
124 private static String[] defaultProtocols(SSLContext context, SSLEngine engine) {
125
126 final String[] supportedProtocols = context.getDefaultSSLParameters().getProtocols();
127 Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length);
128 Collections.addAll(supportedProtocolsSet, supportedProtocols);
129 List<String> protocols = new ArrayList<String>();
130 addIfSupported(
131 supportedProtocolsSet, protocols,
132 SslProtocols.TLS_v1_3, SslProtocols.TLS_v1_2,
133 SslProtocols.TLS_v1_1, SslProtocols.TLS_v1);
134
135 if (!protocols.isEmpty()) {
136 return protocols.toArray(EmptyArrays.EMPTY_STRINGS);
137 }
138 return engine.getEnabledProtocols();
139 }
140
141 private static Set<String> supportedCiphers(SSLEngine engine) {
142
143 final String[] supportedCiphers = engine.getSupportedCipherSuites();
144 Set<String> supportedCiphersSet = new LinkedHashSet<String>(supportedCiphers.length);
145 for (int i = 0; i < supportedCiphers.length; ++i) {
146 String supportedCipher = supportedCiphers[i];
147 supportedCiphersSet.add(supportedCipher);
148
149
150
151
152
153
154
155
156
157 if (supportedCipher.startsWith("SSL_")) {
158 final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length());
159 try {
160 engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName});
161 supportedCiphersSet.add(tlsPrefixedCipherName);
162 } catch (IllegalArgumentException ignored) {
163
164 }
165 }
166 }
167 return supportedCiphersSet;
168 }
169
170 private static List<String> defaultCiphers(SSLEngine engine, Set<String> supportedCiphers) {
171 List<String> ciphers = new ArrayList<String>();
172 addIfSupported(supportedCiphers, ciphers, DEFAULT_CIPHER_SUITES);
173 useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
174 return ciphers;
175 }
176
177 private static boolean isTlsV13Supported(String[] protocols) {
178 for (String protocol: protocols) {
179 if (SslProtocols.TLS_v1_3.equals(protocol)) {
180 return true;
181 }
182 }
183 return false;
184 }
185
186 private final String[] protocols;
187 private final String[] cipherSuites;
188 private final List<String> unmodifiableCipherSuites;
189 @SuppressWarnings("deprecation")
190 private final JdkApplicationProtocolNegotiator apn;
191 private final ClientAuth clientAuth;
192 private final SSLContext sslContext;
193 private final boolean isClient;
194 private final String endpointIdentificationAlgorithm;
195 private final List<SNIServerName> serverNames;
196
197
198
199
200
201
202
203
204
205
206 @Deprecated
207 public JdkSslContext(SSLContext sslContext, boolean isClient,
208 ClientAuth clientAuth) {
209 this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
210 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224
225 @Deprecated
226 public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
227 CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
228 ClientAuth clientAuth) {
229 this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false);
230 }
231
232
233
234
235
236
237
238
239
240
241
242
243
244 public JdkSslContext(SSLContext sslContext,
245 boolean isClient,
246 Iterable<String> ciphers,
247 CipherSuiteFilter cipherFilter,
248 ApplicationProtocolConfig apn,
249 ClientAuth clientAuth,
250 String[] protocols,
251 boolean startTls) {
252 this(sslContext,
253 isClient,
254 ciphers,
255 cipherFilter,
256 toNegotiator(apn, !isClient),
257 clientAuth,
258 protocols == null ? null : protocols.clone(),
259 startTls);
260 }
261
262 @SuppressWarnings("deprecation")
263 JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
264 JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
265 this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, protocols, startTls, null, null, null);
266 }
267
268 @SuppressWarnings("deprecation")
269 JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
270 JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls,
271 String endpointIdentificationAlgorithm, List<SNIServerName> serverNames,
272 ResumptionController resumptionController) {
273 super(startTls, resumptionController);
274 this.apn = checkNotNull(apn, "apn");
275 this.clientAuth = checkNotNull(clientAuth, "clientAuth");
276 this.sslContext = checkNotNull(sslContext, "sslContext");
277 this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
278 this.serverNames = serverNames;
279
280 final List<String> defaultCiphers;
281 final Set<String> supportedCiphers;
282 if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) {
283 this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols;
284 if (isTlsV13Supported(this.protocols)) {
285 supportedCiphers = SUPPORTED_CIPHERS;
286 defaultCiphers = DEFAULT_CIPHERS;
287 } else {
288
289 supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13;
290 defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13;
291 }
292 } else {
293
294
295
296 SSLEngine engine = sslContext.createSSLEngine();
297 try {
298 if (protocols == null) {
299 this.protocols = defaultProtocols(sslContext, engine);
300 } else {
301 this.protocols = protocols;
302 }
303 supportedCiphers = supportedCiphers(engine);
304 defaultCiphers = defaultCiphers(engine, supportedCiphers);
305 if (!isTlsV13Supported(this.protocols)) {
306
307 for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) {
308 supportedCiphers.remove(cipher);
309 defaultCiphers.remove(cipher);
310 }
311 }
312 } finally {
313 ReferenceCountUtil.release(engine);
314 }
315 }
316
317 cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
318 ciphers, defaultCiphers, supportedCiphers);
319
320 unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
321 this.isClient = isClient;
322 }
323
324
325
326
327 public final SSLContext context() {
328 return sslContext;
329 }
330
331 @Override
332 public final boolean isClient() {
333 return isClient;
334 }
335
336
337
338
339 @Override
340 public final SSLSessionContext sessionContext() {
341 if (isServer()) {
342 return context().getServerSessionContext();
343 } else {
344 return context().getClientSessionContext();
345 }
346 }
347
348 @Override
349 public final List<String> cipherSuites() {
350 return unmodifiableCipherSuites;
351 }
352
353 @Override
354 public final SSLEngine newEngine(ByteBufAllocator alloc) {
355 return configureAndWrapEngine(context().createSSLEngine(), alloc);
356 }
357
358 @Override
359 public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
360 return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
361 }
362
363 @SuppressWarnings("deprecation")
364 private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
365 engine.setEnabledCipherSuites(cipherSuites);
366 engine.setEnabledProtocols(protocols);
367 engine.setUseClientMode(isClient());
368 if (isServer()) {
369 switch (clientAuth) {
370 case OPTIONAL:
371 engine.setWantClientAuth(true);
372 break;
373 case REQUIRE:
374 engine.setNeedClientAuth(true);
375 break;
376 case NONE:
377 break;
378 default:
379 throw new Error("Unknown auth " + clientAuth);
380 }
381 }
382 configureSSLParameters(engine);
383 JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
384 if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
385 return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
386 .wrapSslEngine(engine, alloc, apn, isServer());
387 }
388 return factory.wrapSslEngine(engine, apn, isServer());
389 }
390
391 private void configureSSLParameters(SSLEngine engine) {
392 SSLParameters params = engine.getSSLParameters();
393 params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
394 if (serverNames != null && !serverNames.isEmpty()) {
395 params.setServerNames(serverNames);
396 }
397 engine.setSSLParameters(params);
398 }
399
400 @Override
401 public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
402 return apn;
403 }
404
405
406
407
408
409
410
411 @SuppressWarnings("deprecation")
412 static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
413 if (config == null) {
414 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
415 }
416
417 switch(config.protocol()) {
418 case NONE:
419 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
420 case ALPN:
421 if (isServer) {
422 switch(config.selectorFailureBehavior()) {
423 case FATAL_ALERT:
424 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
425 case NO_ADVERTISE:
426 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
427 default:
428 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
429 .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
430 }
431 } else {
432 switch(config.selectedListenerFailureBehavior()) {
433 case ACCEPT:
434 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
435 case FATAL_ALERT:
436 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
437 default:
438 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
439 .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
440 }
441 }
442 default:
443 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
444 .append(config.protocol()).append(" protocol").toString());
445 }
446 }
447
448
449
450
451
452
453
454
455
456
457
458 static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
459 KeyManagerFactory kmf, String keyStore)
460 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
461 NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
462 CertificateException, KeyException, IOException {
463 String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
464 if (algorithm == null) {
465 algorithm = "SunX509";
466 }
467 return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
468 }
469
470
471
472
473
474
475
476
477
478
479
480 @Deprecated
481 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
482 KeyManagerFactory kmf)
483 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
484 NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
485 CertificateException, KeyException, IOException {
486 return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
487 }
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503 static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
504 String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
505 String keyStore)
506 throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
507 InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
508 CertificateException, KeyException, UnrecoverableKeyException {
509 return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
510 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
511 }
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 @Deprecated
528 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
529 String keyAlgorithm, File keyFile,
530 String keyPassword, KeyManagerFactory kmf)
531 throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
532 InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
533 CertificateException, KeyException, UnrecoverableKeyException {
534 return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
535 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
536 }
537 }