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