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