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.internal.tcnative.CertificateVerifier;
21 import io.netty.internal.tcnative.SSL;
22 import io.netty.internal.tcnative.SSLContext;
23 import io.netty.util.AbstractReferenceCounted;
24 import io.netty.util.ReferenceCounted;
25 import io.netty.util.ResourceLeakDetector;
26 import io.netty.util.ResourceLeakDetectorFactory;
27 import io.netty.util.ResourceLeakTracker;
28 import io.netty.util.internal.PlatformDependent;
29 import io.netty.util.internal.StringUtil;
30 import io.netty.util.internal.SystemPropertyUtil;
31 import io.netty.util.internal.logging.InternalLogger;
32 import io.netty.util.internal.logging.InternalLoggerFactory;
33
34 import java.security.AccessController;
35 import java.security.PrivateKey;
36 import java.security.PrivilegedAction;
37 import java.security.cert.CertPathValidatorException;
38 import java.security.cert.Certificate;
39 import java.security.cert.CertificateExpiredException;
40 import java.security.cert.CertificateNotYetValidException;
41 import java.security.cert.CertificateRevokedException;
42 import java.security.cert.X509Certificate;
43 import java.util.Arrays;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.concurrent.locks.Lock;
48 import java.util.concurrent.locks.ReadWriteLock;
49 import java.util.concurrent.locks.ReentrantReadWriteLock;
50
51 import javax.net.ssl.KeyManager;
52 import javax.net.ssl.SSLEngine;
53 import javax.net.ssl.SSLException;
54 import javax.net.ssl.SSLHandshakeException;
55 import javax.net.ssl.TrustManager;
56 import javax.net.ssl.X509ExtendedKeyManager;
57 import javax.net.ssl.X509ExtendedTrustManager;
58 import javax.net.ssl.X509KeyManager;
59 import javax.net.ssl.X509TrustManager;
60
61 import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS;
62 import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites;
63 import static io.netty.util.internal.ObjectUtil.checkNotNull;
64 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
65
66
67
68
69
70
71
72
73
74
75 public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted {
76 private static final InternalLogger logger =
77 InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class);
78
79 private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE =
80 AccessController.doPrivileged(new PrivilegedAction<Integer>() {
81 @Override
82 public Integer run() {
83 return Math.max(1,
84 SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
85 2048));
86 }
87 });
88
89 private static final Integer DH_KEY_LENGTH;
90 private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
91 ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class);
92
93
94 protected static final int VERIFY_DEPTH = 10;
95
96
97
98
99
100
101 protected long ctx;
102 private final List<String> unmodifiableCiphers;
103 private final long sessionCacheSize;
104 private final long sessionTimeout;
105 private final OpenSslApplicationProtocolNegotiator apn;
106 private final int mode;
107
108
109 private final ResourceLeakTracker<ReferenceCountedOpenSslContext> leak;
110 private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() {
111 @Override
112 protected void deallocate() {
113 destroy();
114 if (leak != null) {
115 boolean closed = leak.close(ReferenceCountedOpenSslContext.this);
116 assert closed;
117 }
118 }
119 };
120
121 final Certificate[] keyCertChain;
122 final ClientAuth clientAuth;
123 final String[] protocols;
124 final boolean enableOcsp;
125 final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
126 final ReadWriteLock ctxLock = new ReentrantReadWriteLock();
127
128 private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;
129
130 @SuppressWarnings("deprecation")
131 static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
132 new OpenSslApplicationProtocolNegotiator() {
133 @Override
134 public ApplicationProtocolConfig.Protocol protocol() {
135 return ApplicationProtocolConfig.Protocol.NONE;
136 }
137
138 @Override
139 public List<String> protocols() {
140 return Collections.emptyList();
141 }
142
143 @Override
144 public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
145 return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
146 }
147
148 @Override
149 public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
150 return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
151 }
152 };
153
154 static {
155 Integer dhLen = null;
156
157 try {
158 String dhKeySize = AccessController.doPrivileged(new PrivilegedAction<String>() {
159 @Override
160 public String run() {
161 return SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize");
162 }
163 });
164 if (dhKeySize != null) {
165 try {
166 dhLen = Integer.valueOf(dhKeySize);
167 } catch (NumberFormatException e) {
168 logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: "
169 + dhKeySize);
170 }
171 }
172 } catch (Throwable ignore) {
173
174 }
175 DH_KEY_LENGTH = dhLen;
176 }
177
178 ReferenceCountedOpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
179 ApplicationProtocolConfig apnCfg, long sessionCacheSize, long sessionTimeout,
180 int mode, Certificate[] keyCertChain, ClientAuth clientAuth, String[] protocols,
181 boolean startTls, boolean enableOcsp, boolean leakDetection) throws SSLException {
182 this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode, keyCertChain,
183 clientAuth, protocols, startTls, enableOcsp, leakDetection);
184 }
185
186 ReferenceCountedOpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
187 OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
188 long sessionTimeout, int mode, Certificate[] keyCertChain,
189 ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp,
190 boolean leakDetection) throws SSLException {
191 super(startTls);
192
193 OpenSsl.ensureAvailability();
194
195 if (enableOcsp && !OpenSsl.isOcspSupported()) {
196 throw new IllegalStateException("OCSP is not supported.");
197 }
198
199 if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) {
200 throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT");
201 }
202 leak = leakDetection ? leakDetector.track(this) : null;
203 this.mode = mode;
204 this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
205 this.protocols = protocols;
206 this.enableOcsp = enableOcsp;
207
208 this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone();
209
210 unmodifiableCiphers = Arrays.asList(checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
211 ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites()));
212
213 this.apn = checkNotNull(apn, "apn");
214
215
216 boolean success = false;
217 try {
218 try {
219 ctx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, mode);
220 } catch (Exception e) {
221 throw new SSLException("failed to create an SSL_CTX", e);
222 }
223
224 SSLContext.setOptions(ctx, SSLContext.getOptions(ctx) |
225 SSL.SSL_OP_NO_SSLv2 |
226 SSL.SSL_OP_NO_SSLv3 |
227 SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
228
229
230 SSL.SSL_OP_NO_COMPRESSION |
231
232
233
234
235
236 SSL.SSL_OP_NO_TICKET);
237
238
239
240
241 SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
242
243 if (DH_KEY_LENGTH != null) {
244 SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH);
245 }
246
247
248 try {
249 SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(unmodifiableCiphers));
250 } catch (SSLException e) {
251 throw e;
252 } catch (Exception e) {
253 throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
254 }
255
256 List<String> nextProtoList = apn.protocols();
257
258 if (!nextProtoList.isEmpty()) {
259 String[] appProtocols = nextProtoList.toArray(new String[nextProtoList.size()]);
260 int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
261
262 switch (apn.protocol()) {
263 case NPN:
264 SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
265 break;
266 case ALPN:
267 SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
268 break;
269 case NPN_AND_ALPN:
270 SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
271 SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
272 break;
273 default:
274 throw new Error();
275 }
276 }
277
278
279 if (sessionCacheSize > 0) {
280 this.sessionCacheSize = sessionCacheSize;
281 SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
282 } else {
283
284 this.sessionCacheSize = sessionCacheSize = SSLContext.setSessionCacheSize(ctx, 20480);
285
286 SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
287 }
288
289
290 if (sessionTimeout > 0) {
291 this.sessionTimeout = sessionTimeout;
292 SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
293 } else {
294
295 this.sessionTimeout = sessionTimeout = SSLContext.setSessionCacheTimeout(ctx, 300);
296
297 SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
298 }
299
300 if (enableOcsp) {
301 SSLContext.enableOcsp(ctx, isClient());
302 }
303 success = true;
304 } finally {
305 if (!success) {
306 release();
307 }
308 }
309 }
310
311 private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) {
312 switch (behavior) {
313 case NO_ADVERTISE:
314 return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
315 case CHOOSE_MY_LAST_PROTOCOL:
316 return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
317 default:
318 throw new Error();
319 }
320 }
321
322 @Override
323 public final List<String> cipherSuites() {
324 return unmodifiableCiphers;
325 }
326
327 @Override
328 public final long sessionCacheSize() {
329 return sessionCacheSize;
330 }
331
332 @Override
333 public final long sessionTimeout() {
334 return sessionTimeout;
335 }
336
337 @Override
338 public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
339 return apn;
340 }
341
342 @Override
343 public final boolean isClient() {
344 return mode == SSL.SSL_MODE_CLIENT;
345 }
346
347 @Override
348 public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
349 return newEngine0(alloc, peerHost, peerPort, true);
350 }
351
352 @Override
353 protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
354 return new SslHandler(newEngine0(alloc, null, -1, false), startTls);
355 }
356
357 @Override
358 protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
359 return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls);
360 }
361
362 SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
363 return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true);
364 }
365
366 abstract OpenSslKeyMaterialManager keyMaterialManager();
367
368
369
370
371 @Override
372 public final SSLEngine newEngine(ByteBufAllocator alloc) {
373 return newEngine(alloc, null, -1);
374 }
375
376
377
378
379
380
381
382
383 @Deprecated
384 public final long context() {
385 Lock readerLock = ctxLock.readLock();
386 readerLock.lock();
387 try {
388 return ctx;
389 } finally {
390 readerLock.unlock();
391 }
392 }
393
394
395
396
397
398
399 @Deprecated
400 public final OpenSslSessionStats stats() {
401 return sessionContext().stats();
402 }
403
404
405
406
407
408
409 @Deprecated
410 public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) {
411 if (!rejectRemoteInitiatedRenegotiation) {
412 throw new UnsupportedOperationException("Renegotiation is not supported");
413 }
414 }
415
416
417
418
419
420 @Deprecated
421 public boolean getRejectRemoteInitiatedRenegotiation() {
422 return true;
423 }
424
425
426
427
428
429 public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) {
430 this.bioNonApplicationBufferSize =
431 checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize");
432 }
433
434
435
436
437 public int getBioNonApplicationBufferSize() {
438 return bioNonApplicationBufferSize;
439 }
440
441
442
443
444
445
446 @Deprecated
447 public final void setTicketKeys(byte[] keys) {
448 sessionContext().setTicketKeys(keys);
449 }
450
451 @Override
452 public abstract OpenSslSessionContext sessionContext();
453
454
455
456
457
458
459
460
461 @Deprecated
462 public final long sslCtxPointer() {
463 Lock readerLock = ctxLock.readLock();
464 readerLock.lock();
465 try {
466 return ctx;
467 } finally {
468 readerLock.unlock();
469 }
470 }
471
472
473
474
475 private void destroy() {
476 Lock writerLock = ctxLock.writeLock();
477 writerLock.lock();
478 try {
479 if (ctx != 0) {
480 if (enableOcsp) {
481 SSLContext.disableOcsp(ctx);
482 }
483
484 SSLContext.free(ctx);
485 ctx = 0;
486 }
487 } finally {
488 writerLock.unlock();
489 }
490 }
491
492 protected static X509Certificate[] certificates(byte[][] chain) {
493 X509Certificate[] peerCerts = new X509Certificate[chain.length];
494 for (int i = 0; i < peerCerts.length; i++) {
495 peerCerts[i] = new OpenSslX509Certificate(chain[i]);
496 }
497 return peerCerts;
498 }
499
500 protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
501 for (TrustManager m : managers) {
502 if (m instanceof X509TrustManager) {
503 return (X509TrustManager) m;
504 }
505 }
506 throw new IllegalStateException("no X509TrustManager found");
507 }
508
509 protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) {
510 for (KeyManager km : kms) {
511 if (km instanceof X509KeyManager) {
512 return (X509KeyManager) km;
513 }
514 }
515 throw new IllegalStateException("no X509KeyManager found");
516 }
517
518
519
520
521
522
523
524
525 @SuppressWarnings("deprecation")
526 static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) {
527 if (config == null) {
528 return NONE_PROTOCOL_NEGOTIATOR;
529 }
530
531 switch (config.protocol()) {
532 case NONE:
533 return NONE_PROTOCOL_NEGOTIATOR;
534 case ALPN:
535 case NPN:
536 case NPN_AND_ALPN:
537 switch (config.selectedListenerFailureBehavior()) {
538 case CHOOSE_MY_LAST_PROTOCOL:
539 case ACCEPT:
540 switch (config.selectorFailureBehavior()) {
541 case CHOOSE_MY_LAST_PROTOCOL:
542 case NO_ADVERTISE:
543 return new OpenSslDefaultApplicationProtocolNegotiator(
544 config);
545 default:
546 throw new UnsupportedOperationException(
547 new StringBuilder("OpenSSL provider does not support ")
548 .append(config.selectorFailureBehavior())
549 .append(" behavior").toString());
550 }
551 default:
552 throw new UnsupportedOperationException(
553 new StringBuilder("OpenSSL provider does not support ")
554 .append(config.selectedListenerFailureBehavior())
555 .append(" behavior").toString());
556 }
557 default:
558 throw new Error();
559 }
560 }
561
562 static boolean useExtendedTrustManager(X509TrustManager trustManager) {
563 return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
564 }
565
566 static boolean useExtendedKeyManager(X509KeyManager keyManager) {
567 return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager;
568 }
569
570 @Override
571 public final int refCnt() {
572 return refCnt.refCnt();
573 }
574
575 @Override
576 public final ReferenceCounted retain() {
577 refCnt.retain();
578 return this;
579 }
580
581 @Override
582 public final ReferenceCounted retain(int increment) {
583 refCnt.retain(increment);
584 return this;
585 }
586
587 @Override
588 public final boolean release() {
589 return refCnt.release();
590 }
591
592 @Override
593 public final boolean release(int decrement) {
594 return refCnt.release(decrement);
595 }
596
597 abstract static class AbstractCertificateVerifier extends CertificateVerifier {
598 private final OpenSslEngineMap engineMap;
599
600 AbstractCertificateVerifier(OpenSslEngineMap engineMap) {
601 this.engineMap = engineMap;
602 }
603
604 @Override
605 public final int verify(long ssl, byte[][] chain, String auth) {
606 X509Certificate[] peerCerts = certificates(chain);
607 final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
608 try {
609 verify(engine, peerCerts, auth);
610 return CertificateVerifier.X509_V_OK;
611 } catch (Throwable cause) {
612 logger.debug("verification of certificate failed", cause);
613 SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
614 e.initCause(cause);
615 engine.handshakeException = e;
616
617
618 if (cause instanceof OpenSslCertificateException) {
619
620
621 return ((OpenSslCertificateException) cause).errorCode();
622 }
623 if (cause instanceof CertificateExpiredException) {
624 return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
625 }
626 if (cause instanceof CertificateNotYetValidException) {
627 return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
628 }
629 if (PlatformDependent.javaVersion() >= 7) {
630 if (cause instanceof CertificateRevokedException) {
631 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
632 }
633
634
635
636
637 Throwable wrapped = cause.getCause();
638 while (wrapped != null) {
639 if (wrapped instanceof CertPathValidatorException) {
640 CertPathValidatorException ex = (CertPathValidatorException) wrapped;
641 CertPathValidatorException.Reason reason = ex.getReason();
642 if (reason == CertPathValidatorException.BasicReason.EXPIRED) {
643 return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
644 }
645 if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
646 return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
647 }
648 if (reason == CertPathValidatorException.BasicReason.REVOKED) {
649 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
650 }
651 }
652 wrapped = wrapped.getCause();
653 }
654 }
655
656
657 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
658 }
659 }
660
661 abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts,
662 String auth) throws Exception;
663 }
664
665 private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
666 private final Map<Long, ReferenceCountedOpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
667
668 @Override
669 public ReferenceCountedOpenSslEngine remove(long ssl) {
670 return engines.remove(ssl);
671 }
672
673 @Override
674 public void add(ReferenceCountedOpenSslEngine engine) {
675 engines.put(engine.sslPointer(), engine);
676 }
677
678 @Override
679 public ReferenceCountedOpenSslEngine get(long ssl) {
680 return engines.get(ssl);
681 }
682 }
683
684 static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
685 throws SSLException {
686
687 long keyBio = 0;
688 long keyCertChainBio = 0;
689 long keyCertChainBio2 = 0;
690 PemEncoded encoded = null;
691 try {
692
693 encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain);
694 keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
695 keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
696
697 if (key != null) {
698 keyBio = toBIO(key);
699 }
700
701 SSLContext.setCertificateBio(
702 ctx, keyCertChainBio, keyBio,
703 keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword);
704
705 SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true);
706 } catch (SSLException e) {
707 throw e;
708 } catch (Exception e) {
709 throw new SSLException("failed to set certificate and key", e);
710 } finally {
711 freeBio(keyBio);
712 freeBio(keyCertChainBio);
713 freeBio(keyCertChainBio2);
714 if (encoded != null) {
715 encoded.release();
716 }
717 }
718 }
719
720 static void freeBio(long bio) {
721 if (bio != 0) {
722 SSL.freeBIO(bio);
723 }
724 }
725
726
727
728
729
730 static long toBIO(PrivateKey key) throws Exception {
731 if (key == null) {
732 return 0;
733 }
734
735 ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
736 PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key);
737 try {
738 return toBIO(allocator, pem.retain());
739 } finally {
740 pem.release();
741 }
742 }
743
744
745
746
747
748 static long toBIO(X509Certificate... certChain) throws Exception {
749 if (certChain == null) {
750 return 0;
751 }
752
753 if (certChain.length == 0) {
754 throw new IllegalArgumentException("certChain can't be empty");
755 }
756
757 ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
758 PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain);
759 try {
760 return toBIO(allocator, pem.retain());
761 } finally {
762 pem.release();
763 }
764 }
765
766 static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception {
767 try {
768
769
770 ByteBuf content = pem.content();
771
772 if (content.isDirect()) {
773 return newBIO(content.slice().retain());
774 }
775
776 ByteBuf buffer = allocator.directBuffer(content.readableBytes());
777 try {
778 buffer.writeBytes(content, content.readerIndex(), content.readableBytes());
779 return newBIO(buffer.slice().retain());
780 } finally {
781 try {
782
783
784 if (pem.isSensitive()) {
785 SslUtils.zeroout(buffer);
786 }
787 } finally {
788 buffer.release();
789 }
790 }
791 } finally {
792 pem.release();
793 }
794 }
795
796 private static long newBIO(ByteBuf buffer) throws Exception {
797 try {
798 long bio = SSL.newMemBIO();
799 int readable = buffer.readableBytes();
800 if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
801 SSL.freeBIO(bio);
802 throw new IllegalStateException("Could not write data to memory BIO");
803 }
804 return bio;
805 } finally {
806 buffer.release();
807 }
808 }
809 }