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