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