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.PlatformDependent;
23 import io.netty.util.internal.logging.InternalLogger;
24 import io.netty.util.internal.logging.InternalLoggerFactory;
25
26 import java.io.File;
27 import java.io.IOException;
28 import java.security.InvalidAlgorithmParameterException;
29 import java.security.KeyException;
30 import java.security.KeyStore;
31 import java.security.KeyStoreException;
32 import java.security.NoSuchAlgorithmException;
33 import java.security.Provider;
34 import java.security.Security;
35 import java.security.UnrecoverableKeyException;
36 import java.security.cert.CertificateException;
37 import java.security.spec.InvalidKeySpecException;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.HashSet;
42 import java.util.LinkedHashSet;
43 import java.util.List;
44 import java.util.Set;
45 import javax.crypto.NoSuchPaddingException;
46 import javax.net.ssl.KeyManagerFactory;
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
196
197
198
199
200
201
202
203
204
205 @Deprecated
206 public JdkSslContext(SSLContext sslContext, boolean isClient,
207 ClientAuth clientAuth) {
208 this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE,
209 JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false);
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224 @Deprecated
225 public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers,
226 CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
227 ClientAuth clientAuth) {
228 this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false);
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243 public JdkSslContext(SSLContext sslContext,
244 boolean isClient,
245 Iterable<String> ciphers,
246 CipherSuiteFilter cipherFilter,
247 ApplicationProtocolConfig apn,
248 ClientAuth clientAuth,
249 String[] protocols,
250 boolean startTls) {
251 this(sslContext,
252 isClient,
253 ciphers,
254 cipherFilter,
255 toNegotiator(apn, !isClient),
256 clientAuth,
257 protocols == null ? null : protocols.clone(),
258 startTls);
259 }
260
261 @SuppressWarnings("deprecation")
262 JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
263 JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) {
264 this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, protocols, startTls, null, null);
265 }
266
267 @SuppressWarnings("deprecation")
268 JdkSslContext(SSLContext sslContext, boolean isClient, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
269 JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls,
270 String endpointIdentificationAlgorithm, ResumptionController resumptionController) {
271 super(startTls, resumptionController);
272 this.apn = checkNotNull(apn, "apn");
273 this.clientAuth = checkNotNull(clientAuth, "clientAuth");
274 this.sslContext = checkNotNull(sslContext, "sslContext");
275 this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
276
277 final List<String> defaultCiphers;
278 final Set<String> supportedCiphers;
279 if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) {
280 this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols;
281 if (isTlsV13Supported(this.protocols)) {
282 supportedCiphers = SUPPORTED_CIPHERS;
283 defaultCiphers = DEFAULT_CIPHERS;
284 } else {
285
286 supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13;
287 defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13;
288 }
289 } else {
290
291
292
293 SSLEngine engine = sslContext.createSSLEngine();
294 try {
295 if (protocols == null) {
296 this.protocols = defaultProtocols(sslContext, engine);
297 } else {
298 this.protocols = protocols;
299 }
300 supportedCiphers = supportedCiphers(engine);
301 defaultCiphers = defaultCiphers(engine, supportedCiphers);
302 if (!isTlsV13Supported(this.protocols)) {
303
304 for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) {
305 supportedCiphers.remove(cipher);
306 defaultCiphers.remove(cipher);
307 }
308 }
309 } finally {
310 ReferenceCountUtil.release(engine);
311 }
312 }
313
314 cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
315 ciphers, defaultCiphers, supportedCiphers);
316
317 unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
318 this.isClient = isClient;
319 }
320
321
322
323
324 public final SSLContext context() {
325 return sslContext;
326 }
327
328 @Override
329 public final boolean isClient() {
330 return isClient;
331 }
332
333
334
335
336 @Override
337 public final SSLSessionContext sessionContext() {
338 if (isServer()) {
339 return context().getServerSessionContext();
340 } else {
341 return context().getClientSessionContext();
342 }
343 }
344
345 @Override
346 public final List<String> cipherSuites() {
347 return unmodifiableCipherSuites;
348 }
349
350 @Override
351 public final SSLEngine newEngine(ByteBufAllocator alloc) {
352 return configureAndWrapEngine(context().createSSLEngine(), alloc);
353 }
354
355 @Override
356 public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
357 return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc);
358 }
359
360 @SuppressWarnings("deprecation")
361 private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) {
362 engine.setEnabledCipherSuites(cipherSuites);
363 engine.setEnabledProtocols(protocols);
364 engine.setUseClientMode(isClient());
365 if (isServer()) {
366 switch (clientAuth) {
367 case OPTIONAL:
368 engine.setWantClientAuth(true);
369 break;
370 case REQUIRE:
371 engine.setNeedClientAuth(true);
372 break;
373 case NONE:
374 break;
375 default:
376 throw new Error("Unknown auth " + clientAuth);
377 }
378 }
379 configureEndpointVerification(engine);
380 JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory();
381 if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) {
382 return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory)
383 .wrapSslEngine(engine, alloc, apn, isServer());
384 }
385 return factory.wrapSslEngine(engine, apn, isServer());
386 }
387
388 private void configureEndpointVerification(SSLEngine engine) {
389 int version = PlatformDependent.javaVersion();
390 if (version >= 7) {
391 SSLParameters params = engine.getSSLParameters();
392 Java7SslParametersUtils.setEndpointIdentificationAlgorithm(params, endpointIdentificationAlgorithm);
393 engine.setSSLParameters(params);
394 }
395 }
396
397 @Override
398 public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
399 return apn;
400 }
401
402
403
404
405
406
407
408 @SuppressWarnings("deprecation")
409 static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
410 if (config == null) {
411 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
412 }
413
414 switch(config.protocol()) {
415 case NONE:
416 return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
417 case ALPN:
418 if (isServer) {
419 switch(config.selectorFailureBehavior()) {
420 case FATAL_ALERT:
421 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
422 case NO_ADVERTISE:
423 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
424 default:
425 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
426 .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
427 }
428 } else {
429 switch(config.selectedListenerFailureBehavior()) {
430 case ACCEPT:
431 return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
432 case FATAL_ALERT:
433 return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
434 default:
435 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
436 .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
437 }
438 }
439 case NPN:
440 if (isServer) {
441 switch(config.selectedListenerFailureBehavior()) {
442 case ACCEPT:
443 return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
444 case FATAL_ALERT:
445 return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
446 default:
447 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
448 .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
449 }
450 } else {
451 switch(config.selectorFailureBehavior()) {
452 case FATAL_ALERT:
453 return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
454 case NO_ADVERTISE:
455 return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
456 default:
457 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
458 .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
459 }
460 }
461 default:
462 throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
463 .append(config.protocol()).append(" protocol").toString());
464 }
465 }
466
467
468
469
470
471
472
473
474
475
476
477 static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
478 KeyManagerFactory kmf, String keyStore)
479 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
480 NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
481 CertificateException, KeyException, IOException {
482 String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
483 if (algorithm == null) {
484 algorithm = "SunX509";
485 }
486 return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
487 }
488
489
490
491
492
493
494
495
496
497
498
499 @Deprecated
500 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
501 KeyManagerFactory kmf)
502 throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
503 NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
504 CertificateException, KeyException, IOException {
505 return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
506 }
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522 static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
523 String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
524 String keyStore)
525 throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
526 InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
527 CertificateException, KeyException, UnrecoverableKeyException {
528 return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
529 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
530 }
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 @Deprecated
547 protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
548 String keyAlgorithm, File keyFile,
549 String keyPassword, KeyManagerFactory kmf)
550 throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
551 InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
552 CertificateException, KeyException, UnrecoverableKeyException {
553 return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
554 toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
555 }
556 }