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