1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.handler.ssl.util.LazyX509Certificate;
21 import io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod;
22 import io.netty.internal.tcnative.CertificateCompressionAlgo;
23 import io.netty.internal.tcnative.CertificateVerifier;
24 import io.netty.internal.tcnative.ResultCallback;
25 import io.netty.internal.tcnative.SSL;
26 import io.netty.internal.tcnative.SSLContext;
27 import io.netty.internal.tcnative.SSLPrivateKeyMethod;
28 import io.netty.util.AbstractReferenceCounted;
29 import io.netty.util.ReferenceCounted;
30 import io.netty.util.ResourceLeakDetector;
31 import io.netty.util.ResourceLeakDetectorFactory;
32 import io.netty.util.ResourceLeakTracker;
33 import io.netty.util.concurrent.Future;
34 import io.netty.util.concurrent.FutureListener;
35 import io.netty.util.concurrent.ImmediateExecutor;
36 import io.netty.util.internal.EmptyArrays;
37 import io.netty.util.internal.PlatformDependent;
38 import io.netty.util.internal.StringUtil;
39 import io.netty.util.internal.SuppressJava6Requirement;
40 import io.netty.util.internal.SystemPropertyUtil;
41 import io.netty.util.internal.UnstableApi;
42 import io.netty.util.internal.logging.InternalLogger;
43 import io.netty.util.internal.logging.InternalLoggerFactory;
44
45 import java.security.PrivateKey;
46 import java.security.SignatureException;
47 import java.security.cert.CertPathValidatorException;
48 import java.security.cert.Certificate;
49 import java.security.cert.CertificateExpiredException;
50 import java.security.cert.CertificateNotYetValidException;
51 import java.security.cert.CertificateRevokedException;
52 import java.security.cert.X509Certificate;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.LinkedHashSet;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.concurrent.Executor;
59 import java.util.concurrent.locks.Lock;
60 import java.util.concurrent.locks.ReadWriteLock;
61 import java.util.concurrent.locks.ReentrantReadWriteLock;
62
63 import javax.net.ssl.KeyManager;
64 import javax.net.ssl.KeyManagerFactory;
65 import javax.net.ssl.SSLEngine;
66 import javax.net.ssl.SSLException;
67 import javax.net.ssl.SSLHandshakeException;
68 import javax.net.ssl.TrustManager;
69 import javax.net.ssl.X509ExtendedTrustManager;
70 import javax.net.ssl.X509KeyManager;
71 import javax.net.ssl.X509TrustManager;
72
73 import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS;
74 import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites;
75 import static io.netty.util.internal.ObjectUtil.checkNotNull;
76 import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
77 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
78
79
80
81
82
83
84
85
86
87
88 public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted {
89 private static final InternalLogger logger =
90 InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class);
91
92 private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE = Math.max(1,
93 SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
94 2048));
95
96 static final boolean USE_TASKS =
97 SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", true);
98 private static final Integer DH_KEY_LENGTH;
99 private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
100 ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class);
101
102
103 protected static final int VERIFY_DEPTH = 10;
104
105 static final boolean CLIENT_ENABLE_SESSION_TICKET =
106 SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", false);
107
108 static final boolean CLIENT_ENABLE_SESSION_TICKET_TLSV13 =
109 SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", true);
110
111 static final boolean SERVER_ENABLE_SESSION_TICKET =
112 SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", false);
113
114 static final boolean SERVER_ENABLE_SESSION_TICKET_TLSV13 =
115 SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", true);
116
117 static final boolean SERVER_ENABLE_SESSION_CACHE =
118 SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheServer", true);
119 static final boolean CLIENT_ENABLE_SESSION_CACHE =
120 SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheClient", true);
121
122
123
124
125
126
127 protected long ctx;
128 private final List<String> unmodifiableCiphers;
129 private final OpenSslApplicationProtocolNegotiator apn;
130 private final int mode;
131
132
133 private final ResourceLeakTracker<ReferenceCountedOpenSslContext> leak;
134 private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() {
135 @Override
136 public ReferenceCounted touch(Object hint) {
137 if (leak != null) {
138 leak.record(hint);
139 }
140
141 return ReferenceCountedOpenSslContext.this;
142 }
143
144 @Override
145 protected void deallocate() {
146 destroy();
147 if (leak != null) {
148 boolean closed = leak.close(ReferenceCountedOpenSslContext.this);
149 assert closed;
150 }
151 }
152 };
153
154 final Certificate[] keyCertChain;
155 final ClientAuth clientAuth;
156 final String[] protocols;
157 final String endpointIdentificationAlgorithm;
158 final boolean hasTLSv13Cipher;
159
160 final boolean enableOcsp;
161 final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
162 final ReadWriteLock ctxLock = new ReentrantReadWriteLock();
163
164 private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;
165
166 @SuppressWarnings("deprecation")
167 static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
168 new OpenSslApplicationProtocolNegotiator() {
169 @Override
170 public ApplicationProtocolConfig.Protocol protocol() {
171 return ApplicationProtocolConfig.Protocol.NONE;
172 }
173
174 @Override
175 public List<String> protocols() {
176 return Collections.emptyList();
177 }
178
179 @Override
180 public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
181 return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
182 }
183
184 @Override
185 public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
186 return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
187 }
188 };
189
190 static {
191 Integer dhLen = null;
192
193 try {
194 String dhKeySize = SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize");
195 if (dhKeySize != null) {
196 try {
197 dhLen = Integer.valueOf(dhKeySize);
198 } catch (NumberFormatException e) {
199 logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: "
200 + dhKeySize);
201 }
202 }
203 } catch (Throwable ignore) {
204
205 }
206 DH_KEY_LENGTH = dhLen;
207 }
208
209 final boolean tlsFalseStart;
210
211 ReferenceCountedOpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
212 OpenSslApplicationProtocolNegotiator apn, int mode, Certificate[] keyCertChain,
213 ClientAuth clientAuth, String[] protocols, boolean startTls,
214 String endpointIdentificationAlgorithm, boolean enableOcsp,
215 boolean leakDetection, ResumptionController resumptionController,
216 Map.Entry<SslContextOption<?>, Object>... ctxOptions)
217 throws SSLException {
218 super(startTls, resumptionController);
219
220 OpenSsl.ensureAvailability();
221
222 if (enableOcsp && !OpenSsl.isOcspSupported()) {
223 throw new IllegalStateException("OCSP is not supported.");
224 }
225
226 if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) {
227 throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT");
228 }
229
230 boolean tlsFalseStart = false;
231 boolean useTasks = USE_TASKS;
232 OpenSslPrivateKeyMethod privateKeyMethod = null;
233 OpenSslAsyncPrivateKeyMethod asyncPrivateKeyMethod = null;
234 OpenSslCertificateCompressionConfig certCompressionConfig = null;
235 Integer maxCertificateList = null;
236
237 if (ctxOptions != null) {
238 for (Map.Entry<SslContextOption<?>, Object> ctxOpt : ctxOptions) {
239 SslContextOption<?> option = ctxOpt.getKey();
240
241 if (option == OpenSslContextOption.TLS_FALSE_START) {
242 tlsFalseStart = (Boolean) ctxOpt.getValue();
243 } else if (option == OpenSslContextOption.USE_TASKS) {
244 useTasks = (Boolean) ctxOpt.getValue();
245 } else if (option == OpenSslContextOption.PRIVATE_KEY_METHOD) {
246 privateKeyMethod = (OpenSslPrivateKeyMethod) ctxOpt.getValue();
247 } else if (option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD) {
248 asyncPrivateKeyMethod = (OpenSslAsyncPrivateKeyMethod) ctxOpt.getValue();
249 } else if (option == OpenSslContextOption.CERTIFICATE_COMPRESSION_ALGORITHMS) {
250 certCompressionConfig = (OpenSslCertificateCompressionConfig) ctxOpt.getValue();
251 } else if (option == OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES) {
252 maxCertificateList = (Integer) ctxOpt.getValue();
253 } else {
254 logger.debug("Skipping unsupported " + SslContextOption.class.getSimpleName()
255 + ": " + ctxOpt.getKey());
256 }
257 }
258 }
259 if (privateKeyMethod != null && asyncPrivateKeyMethod != null) {
260 throw new IllegalArgumentException("You can either only use "
261 + OpenSslAsyncPrivateKeyMethod.class.getSimpleName() + " or "
262 + OpenSslPrivateKeyMethod.class.getSimpleName());
263 }
264
265 this.tlsFalseStart = tlsFalseStart;
266
267 leak = leakDetection ? leakDetector.track(this) : null;
268 this.mode = mode;
269 this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
270 this.protocols = protocols == null ? OpenSsl.defaultProtocols(mode == SSL.SSL_MODE_CLIENT) : protocols;
271 this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
272 this.enableOcsp = enableOcsp;
273
274 this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone();
275
276 String[] suites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
277 ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites());
278
279 LinkedHashSet<String> suitesSet = new LinkedHashSet<String>(suites.length);
280 Collections.addAll(suitesSet, suites);
281 unmodifiableCiphers = new ArrayList<String>(suitesSet);
282
283 this.apn = checkNotNull(apn, "apn");
284
285
286 boolean success = false;
287 try {
288 boolean tlsv13Supported = OpenSsl.isTlsv13Supported();
289 boolean anyTlsv13Ciphers = false;
290 try {
291 int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 |
292 SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2;
293 if (tlsv13Supported) {
294 protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3;
295 }
296 ctx = SSLContext.make(protocolOpts, mode);
297 } catch (Exception e) {
298 throw new SSLException("failed to create an SSL_CTX", e);
299 }
300
301 StringBuilder cipherBuilder = new StringBuilder();
302 StringBuilder cipherTLSv13Builder = new StringBuilder();
303
304
305 try {
306 if (unmodifiableCiphers.isEmpty()) {
307
308 SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false);
309 if (tlsv13Supported) {
310
311 SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true);
312 }
313 } else {
314 CipherSuiteConverter.convertToCipherStrings(
315 unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder, OpenSsl.isBoringSSL());
316
317
318 SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);
319 if (tlsv13Supported) {
320
321 String tlsv13Ciphers = OpenSsl.checkTls13Ciphers(logger, cipherTLSv13Builder.toString());
322 SSLContext.setCipherSuite(ctx, tlsv13Ciphers, true);
323 if (!tlsv13Ciphers.isEmpty()) {
324 anyTlsv13Ciphers = true;
325 }
326 }
327 }
328 } catch (SSLException e) {
329 throw e;
330 } catch (Exception e) {
331 throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
332 }
333
334 int options = SSLContext.getOptions(ctx) |
335 SSL.SSL_OP_NO_SSLv2 |
336 SSL.SSL_OP_NO_SSLv3 |
337
338
339
340 SSL.SSL_OP_NO_TLSv1 |
341 SSL.SSL_OP_NO_TLSv1_1 |
342
343 SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
344
345
346 SSL.SSL_OP_NO_COMPRESSION |
347
348
349
350
351
352 SSL.SSL_OP_NO_TICKET;
353
354 if (cipherBuilder.length() == 0) {
355
356 options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1
357 | SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2;
358 }
359
360 if (!tlsv13Supported) {
361
362
363
364 options |= SSL.SSL_OP_NO_TLSv1_3;
365 }
366
367 hasTLSv13Cipher = anyTlsv13Ciphers;
368 SSLContext.setOptions(ctx, options);
369
370
371
372
373 SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
374
375 if (DH_KEY_LENGTH != null) {
376 SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH);
377 }
378
379 List<String> nextProtoList = apn.protocols();
380
381 if (!nextProtoList.isEmpty()) {
382 String[] appProtocols = nextProtoList.toArray(EmptyArrays.EMPTY_STRINGS);
383 int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
384
385 switch (apn.protocol()) {
386 case NPN:
387 SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
388 break;
389 case ALPN:
390 SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
391 break;
392 case NPN_AND_ALPN:
393 SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
394 SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
395 break;
396 default:
397 throw new Error();
398 }
399 }
400
401 if (enableOcsp) {
402 SSLContext.enableOcsp(ctx, isClient());
403 }
404
405 SSLContext.setUseTasks(ctx, useTasks);
406 if (privateKeyMethod != null) {
407 SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, privateKeyMethod));
408 }
409 if (asyncPrivateKeyMethod != null) {
410 SSLContext.setPrivateKeyMethod(ctx, new AsyncPrivateKeyMethod(engineMap, asyncPrivateKeyMethod));
411 }
412 if (certCompressionConfig != null) {
413 for (OpenSslCertificateCompressionConfig.AlgorithmConfig configPair : certCompressionConfig) {
414 final CertificateCompressionAlgo algo = new CompressionAlgorithm(engineMap, configPair.algorithm());
415 switch (configPair.mode()) {
416 case Decompress:
417 SSLContext.addCertificateCompressionAlgorithm(
418 ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_DECOMPRESS, algo);
419 break;
420 case Compress:
421 SSLContext.addCertificateCompressionAlgorithm(
422 ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_COMPRESS, algo);
423 break;
424 case Both:
425 SSLContext.addCertificateCompressionAlgorithm(
426 ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_BOTH, algo);
427 break;
428 default:
429 throw new IllegalStateException();
430 }
431 }
432 }
433 if (maxCertificateList != null) {
434 SSLContext.setMaxCertList(ctx, maxCertificateList);
435 }
436
437 SSLContext.setCurvesList(ctx, OpenSsl.NAMED_GROUPS);
438 success = true;
439 } finally {
440 if (!success) {
441 release();
442 }
443 }
444 }
445
446 private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) {
447 switch (behavior) {
448 case NO_ADVERTISE:
449 return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
450 case CHOOSE_MY_LAST_PROTOCOL:
451 return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
452 default:
453 throw new Error();
454 }
455 }
456
457 @Override
458 public final List<String> cipherSuites() {
459 return unmodifiableCiphers;
460 }
461
462 @Override
463 public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
464 return apn;
465 }
466
467 @Override
468 public final boolean isClient() {
469 return mode == SSL.SSL_MODE_CLIENT;
470 }
471
472 @Override
473 public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
474 return newEngine0(alloc, peerHost, peerPort, true);
475 }
476
477 @Override
478 protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
479 return new SslHandler(newEngine0(alloc, null, -1, false), startTls, ImmediateExecutor.INSTANCE,
480 resumptionController);
481 }
482
483 @Override
484 protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
485 return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls, ImmediateExecutor.INSTANCE,
486 resumptionController);
487 }
488
489 @Override
490 protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) {
491 return new SslHandler(newEngine0(alloc, null, -1, false), startTls, executor, resumptionController);
492 }
493
494 @Override
495 protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
496 boolean startTls, Executor executor) {
497 return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), false, executor, resumptionController);
498 }
499
500 SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
501 return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true,
502 endpointIdentificationAlgorithm);
503 }
504
505
506
507
508 @Override
509 public final SSLEngine newEngine(ByteBufAllocator alloc) {
510 return newEngine(alloc, null, -1);
511 }
512
513
514
515
516
517
518
519
520 @Deprecated
521 public final long context() {
522 return sslCtxPointer();
523 }
524
525
526
527
528
529
530 @Deprecated
531 public final OpenSslSessionStats stats() {
532 return sessionContext().stats();
533 }
534
535
536
537
538
539
540 @Deprecated
541 public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) {
542 if (!rejectRemoteInitiatedRenegotiation) {
543 throw new UnsupportedOperationException("Renegotiation is not supported");
544 }
545 }
546
547
548
549
550
551 @Deprecated
552 public boolean getRejectRemoteInitiatedRenegotiation() {
553 return true;
554 }
555
556
557
558
559
560 public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) {
561 this.bioNonApplicationBufferSize =
562 checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize");
563 }
564
565
566
567
568 public int getBioNonApplicationBufferSize() {
569 return bioNonApplicationBufferSize;
570 }
571
572
573
574
575
576
577 @Deprecated
578 public final void setTicketKeys(byte[] keys) {
579 sessionContext().setTicketKeys(keys);
580 }
581
582 @Override
583 public abstract OpenSslSessionContext sessionContext();
584
585
586
587
588
589
590
591
592 @Deprecated
593 public final long sslCtxPointer() {
594 Lock readerLock = ctxLock.readLock();
595 readerLock.lock();
596 try {
597 return SSLContext.getSslCtx(ctx);
598 } finally {
599 readerLock.unlock();
600 }
601 }
602
603
604
605
606
607
608
609
610
611
612
613 @Deprecated
614 @UnstableApi
615 public final void setPrivateKeyMethod(OpenSslPrivateKeyMethod method) {
616 checkNotNull(method, "method");
617 Lock writerLock = ctxLock.writeLock();
618 writerLock.lock();
619 try {
620 SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, method));
621 } finally {
622 writerLock.unlock();
623 }
624 }
625
626
627
628
629
630 @Deprecated
631 public final void setUseTasks(boolean useTasks) {
632 Lock writerLock = ctxLock.writeLock();
633 writerLock.lock();
634 try {
635 SSLContext.setUseTasks(ctx, useTasks);
636 } finally {
637 writerLock.unlock();
638 }
639 }
640
641
642
643
644 private void destroy() {
645 Lock writerLock = ctxLock.writeLock();
646 writerLock.lock();
647 try {
648 if (ctx != 0) {
649 if (enableOcsp) {
650 SSLContext.disableOcsp(ctx);
651 }
652
653 SSLContext.free(ctx);
654 ctx = 0;
655
656 OpenSslSessionContext context = sessionContext();
657 if (context != null) {
658 context.destroy();
659 }
660 }
661 } finally {
662 writerLock.unlock();
663 }
664 }
665
666 protected static X509Certificate[] certificates(byte[][] chain) {
667 X509Certificate[] peerCerts = new X509Certificate[chain.length];
668 for (int i = 0; i < peerCerts.length; i++) {
669 peerCerts[i] = new LazyX509Certificate(chain[i]);
670 }
671 return peerCerts;
672 }
673
674
675
676
677 @Deprecated
678 protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
679 return chooseTrustManager(managers, null);
680 }
681
682 static X509TrustManager chooseTrustManager(TrustManager[] managers,
683 ResumptionController resumptionController) {
684 for (TrustManager m : managers) {
685 if (m instanceof X509TrustManager) {
686 X509TrustManager tm = (X509TrustManager) m;
687 if (PlatformDependent.javaVersion() >= 7) {
688 if (resumptionController != null) {
689 tm = (X509TrustManager) resumptionController.wrapIfNeeded(tm);
690 }
691 tm = OpenSslX509TrustManagerWrapper.wrapIfNeeded(tm);
692 if (useExtendedTrustManager(tm)) {
693
694
695 tm = new EnhancingX509ExtendedTrustManager(tm);
696 }
697 }
698 return tm;
699 }
700 }
701 throw new IllegalStateException("no X509TrustManager found");
702 }
703
704 protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) {
705 for (KeyManager km : kms) {
706 if (km instanceof X509KeyManager) {
707 return (X509KeyManager) km;
708 }
709 }
710 throw new IllegalStateException("no X509KeyManager found");
711 }
712
713
714
715
716
717
718
719
720 @SuppressWarnings("deprecation")
721 static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) {
722 if (config == null) {
723 return NONE_PROTOCOL_NEGOTIATOR;
724 }
725
726 switch (config.protocol()) {
727 case NONE:
728 return NONE_PROTOCOL_NEGOTIATOR;
729 case ALPN:
730 case NPN:
731 case NPN_AND_ALPN:
732 switch (config.selectedListenerFailureBehavior()) {
733 case CHOOSE_MY_LAST_PROTOCOL:
734 case ACCEPT:
735 switch (config.selectorFailureBehavior()) {
736 case CHOOSE_MY_LAST_PROTOCOL:
737 case NO_ADVERTISE:
738 return new OpenSslDefaultApplicationProtocolNegotiator(
739 config);
740 default:
741 throw new UnsupportedOperationException(
742 new StringBuilder("OpenSSL provider does not support ")
743 .append(config.selectorFailureBehavior())
744 .append(" behavior").toString());
745 }
746 default:
747 throw new UnsupportedOperationException(
748 new StringBuilder("OpenSSL provider does not support ")
749 .append(config.selectedListenerFailureBehavior())
750 .append(" behavior").toString());
751 }
752 default:
753 throw new Error();
754 }
755 }
756
757 @SuppressJava6Requirement(reason = "Guarded by java version check")
758 static boolean useExtendedTrustManager(X509TrustManager trustManager) {
759 return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
760 }
761
762 @Override
763 public final int refCnt() {
764 return refCnt.refCnt();
765 }
766
767 @Override
768 public final ReferenceCounted retain() {
769 refCnt.retain();
770 return this;
771 }
772
773 @Override
774 public final ReferenceCounted retain(int increment) {
775 refCnt.retain(increment);
776 return this;
777 }
778
779 @Override
780 public final ReferenceCounted touch() {
781 refCnt.touch();
782 return this;
783 }
784
785 @Override
786 public final ReferenceCounted touch(Object hint) {
787 refCnt.touch(hint);
788 return this;
789 }
790
791 @Override
792 public final boolean release() {
793 return refCnt.release();
794 }
795
796 @Override
797 public final boolean release(int decrement) {
798 return refCnt.release(decrement);
799 }
800
801 abstract static class AbstractCertificateVerifier extends CertificateVerifier {
802 private final OpenSslEngineMap engineMap;
803
804 AbstractCertificateVerifier(OpenSslEngineMap engineMap) {
805 this.engineMap = engineMap;
806 }
807
808 @Override
809 public final int verify(long ssl, byte[][] chain, String auth) {
810 final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
811 if (engine == null) {
812
813 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
814 }
815 X509Certificate[] peerCerts = certificates(chain);
816 try {
817 verify(engine, peerCerts, auth);
818 return CertificateVerifier.X509_V_OK;
819 } catch (Throwable cause) {
820 logger.debug("verification of certificate failed", cause);
821 engine.initHandshakeException(cause);
822
823
824 if (cause instanceof OpenSslCertificateException) {
825
826
827 return ((OpenSslCertificateException) cause).errorCode();
828 }
829 if (cause instanceof CertificateExpiredException) {
830 return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
831 }
832 if (cause instanceof CertificateNotYetValidException) {
833 return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
834 }
835 if (PlatformDependent.javaVersion() >= 7) {
836 return translateToError(cause);
837 }
838
839
840 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
841 }
842 }
843
844 @SuppressJava6Requirement(reason = "Usage guarded by java version check")
845 private static int translateToError(Throwable cause) {
846 if (cause instanceof CertificateRevokedException) {
847 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
848 }
849
850
851
852
853 Throwable wrapped = cause.getCause();
854 while (wrapped != null) {
855 if (wrapped instanceof CertPathValidatorException) {
856 CertPathValidatorException ex = (CertPathValidatorException) wrapped;
857 CertPathValidatorException.Reason reason = ex.getReason();
858 if (reason == CertPathValidatorException.BasicReason.EXPIRED) {
859 return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
860 }
861 if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
862 return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
863 }
864 if (reason == CertPathValidatorException.BasicReason.REVOKED) {
865 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
866 }
867 }
868 wrapped = wrapped.getCause();
869 }
870 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
871 }
872
873 abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts,
874 String auth) throws Exception;
875 }
876
877 private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
878 private final Map<Long, ReferenceCountedOpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
879
880 @Override
881 public ReferenceCountedOpenSslEngine remove(long ssl) {
882 return engines.remove(ssl);
883 }
884
885 @Override
886 public void add(ReferenceCountedOpenSslEngine engine) {
887 engines.put(engine.sslPointer(), engine);
888 }
889
890 @Override
891 public ReferenceCountedOpenSslEngine get(long ssl) {
892 return engines.get(ssl);
893 }
894 }
895
896 static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
897 throws SSLException {
898
899 long keyBio = 0;
900 long keyCertChainBio = 0;
901 long keyCertChainBio2 = 0;
902 PemEncoded encoded = null;
903 try {
904
905 encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain);
906 keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
907 keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
908
909 if (key != null) {
910 keyBio = toBIO(ByteBufAllocator.DEFAULT, key);
911 }
912
913 SSLContext.setCertificateBio(
914 ctx, keyCertChainBio, keyBio,
915 keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword);
916
917 SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true);
918 } catch (SSLException e) {
919 throw e;
920 } catch (Exception e) {
921 throw new SSLException("failed to set certificate and key", e);
922 } finally {
923 freeBio(keyBio);
924 freeBio(keyCertChainBio);
925 freeBio(keyCertChainBio2);
926 if (encoded != null) {
927 encoded.release();
928 }
929 }
930 }
931
932 static void freeBio(long bio) {
933 if (bio != 0) {
934 SSL.freeBIO(bio);
935 }
936 }
937
938
939
940
941
942 static long toBIO(ByteBufAllocator allocator, PrivateKey key) throws Exception {
943 if (key == null) {
944 return 0;
945 }
946
947 PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key);
948 try {
949 return toBIO(allocator, pem.retain());
950 } finally {
951 pem.release();
952 }
953 }
954
955
956
957
958
959 static long toBIO(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception {
960 if (certChain == null) {
961 return 0;
962 }
963
964 checkNonEmpty(certChain, "certChain");
965
966 PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain);
967 try {
968 return toBIO(allocator, pem.retain());
969 } finally {
970 pem.release();
971 }
972 }
973
974 static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception {
975 try {
976
977
978 ByteBuf content = pem.content();
979
980 if (content.isDirect()) {
981 return newBIO(content.retainedSlice());
982 }
983
984 ByteBuf buffer = allocator.directBuffer(content.readableBytes());
985 try {
986 buffer.writeBytes(content, content.readerIndex(), content.readableBytes());
987 return newBIO(buffer.retainedSlice());
988 } finally {
989 try {
990
991
992 if (pem.isSensitive()) {
993 SslUtils.zeroout(buffer);
994 }
995 } finally {
996 buffer.release();
997 }
998 }
999 } finally {
1000 pem.release();
1001 }
1002 }
1003
1004 private static long newBIO(ByteBuf buffer) throws Exception {
1005 try {
1006 long bio = SSL.newMemBIO();
1007 int readable = buffer.readableBytes();
1008 if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
1009 SSL.freeBIO(bio);
1010 throw new IllegalStateException("Could not write data to memory BIO");
1011 }
1012 return bio;
1013 } finally {
1014 buffer.release();
1015 }
1016 }
1017
1018
1019
1020
1021
1022
1023 static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) {
1024 if (factory instanceof OpenSslX509KeyManagerFactory) {
1025 return ((OpenSslX509KeyManagerFactory) factory).newProvider();
1026 }
1027
1028 if (factory instanceof OpenSslCachingX509KeyManagerFactory) {
1029
1030 return ((OpenSslCachingX509KeyManagerFactory) factory).newProvider(password);
1031 }
1032
1033 return new OpenSslKeyMaterialProvider(chooseX509KeyManager(factory.getKeyManagers()), password);
1034 }
1035
1036 private static ReferenceCountedOpenSslEngine retrieveEngine(OpenSslEngineMap engineMap, long ssl)
1037 throws SSLException {
1038 ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
1039 if (engine == null) {
1040 throw new SSLException("Could not find a " +
1041 StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl);
1042 }
1043 return engine;
1044 }
1045
1046 private static final class PrivateKeyMethod implements SSLPrivateKeyMethod {
1047
1048 private final OpenSslEngineMap engineMap;
1049 private final OpenSslPrivateKeyMethod keyMethod;
1050 PrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslPrivateKeyMethod keyMethod) {
1051 this.engineMap = engineMap;
1052 this.keyMethod = keyMethod;
1053 }
1054
1055 @Override
1056 public byte[] sign(long ssl, int signatureAlgorithm, byte[] digest) throws Exception {
1057 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1058 try {
1059 return verifyResult(keyMethod.sign(engine, signatureAlgorithm, digest));
1060 } catch (Exception e) {
1061 engine.initHandshakeException(e);
1062 throw e;
1063 }
1064 }
1065
1066 @Override
1067 public byte[] decrypt(long ssl, byte[] input) throws Exception {
1068 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1069 try {
1070 return verifyResult(keyMethod.decrypt(engine, input));
1071 } catch (Exception e) {
1072 engine.initHandshakeException(e);
1073 throw e;
1074 }
1075 }
1076 }
1077
1078 private static final class AsyncPrivateKeyMethod implements AsyncSSLPrivateKeyMethod {
1079
1080 private final OpenSslEngineMap engineMap;
1081 private final OpenSslAsyncPrivateKeyMethod keyMethod;
1082
1083 AsyncPrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslAsyncPrivateKeyMethod keyMethod) {
1084 this.engineMap = engineMap;
1085 this.keyMethod = keyMethod;
1086 }
1087
1088 @Override
1089 public void sign(long ssl, int signatureAlgorithm, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1090 try {
1091 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1092 keyMethod.sign(engine, signatureAlgorithm, bytes)
1093 .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1094 } catch (SSLException e) {
1095 resultCallback.onError(ssl, e);
1096 }
1097 }
1098
1099 @Override
1100 public void decrypt(long ssl, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1101 try {
1102 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1103 keyMethod.decrypt(engine, bytes)
1104 .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1105 } catch (SSLException e) {
1106 resultCallback.onError(ssl, e);
1107 }
1108 }
1109
1110 private static final class ResultCallbackListener implements FutureListener<byte[]> {
1111 private final ReferenceCountedOpenSslEngine engine;
1112 private final long ssl;
1113 private final ResultCallback<byte[]> resultCallback;
1114
1115 ResultCallbackListener(ReferenceCountedOpenSslEngine engine, long ssl,
1116 ResultCallback<byte[]> resultCallback) {
1117 this.engine = engine;
1118 this.ssl = ssl;
1119 this.resultCallback = resultCallback;
1120 }
1121
1122 @Override
1123 public void operationComplete(Future<byte[]> future) {
1124 Throwable cause = future.cause();
1125 if (cause == null) {
1126 try {
1127 byte[] result = verifyResult(future.getNow());
1128 resultCallback.onSuccess(ssl, result);
1129 return;
1130 } catch (SignatureException e) {
1131 cause = e;
1132 engine.initHandshakeException(e);
1133 }
1134 }
1135 resultCallback.onError(ssl, cause);
1136 }
1137 }
1138 }
1139
1140 private static byte[] verifyResult(byte[] result) throws SignatureException {
1141 if (result == null) {
1142 throw new SignatureException();
1143 }
1144 return result;
1145 }
1146
1147 private static final class CompressionAlgorithm implements CertificateCompressionAlgo {
1148 private final OpenSslEngineMap engineMap;
1149 private final OpenSslCertificateCompressionAlgorithm compressionAlgorithm;
1150
1151 CompressionAlgorithm(OpenSslEngineMap engineMap, OpenSslCertificateCompressionAlgorithm compressionAlgorithm) {
1152 this.engineMap = engineMap;
1153 this.compressionAlgorithm = compressionAlgorithm;
1154 }
1155
1156 @Override
1157 public byte[] compress(long ssl, byte[] bytes) throws Exception {
1158 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1159 return compressionAlgorithm.compress(engine, bytes);
1160 }
1161
1162 @Override
1163 public byte[] decompress(long ssl, int len, byte[] bytes) throws Exception {
1164 ReferenceCountedOpenSslEngine engine = retrieveEngine(engineMap, ssl);
1165 return compressionAlgorithm.decompress(engine, len, bytes);
1166 }
1167
1168 @Override
1169 public int algorithmId() {
1170 return compressionAlgorithm.algorithmId();
1171 }
1172 }
1173 }