View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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.CertificateCallback;
23  import io.netty.internal.tcnative.CertificateCompressionAlgo;
24  import io.netty.internal.tcnative.CertificateVerifier;
25  import io.netty.internal.tcnative.ResultCallback;
26  import io.netty.internal.tcnative.SSL;
27  import io.netty.internal.tcnative.SSLContext;
28  import io.netty.internal.tcnative.SSLPrivateKeyMethod;
29  import io.netty.util.AbstractReferenceCounted;
30  import io.netty.util.ReferenceCounted;
31  import io.netty.util.ResourceLeakDetector;
32  import io.netty.util.ResourceLeakDetectorFactory;
33  import io.netty.util.ResourceLeakTracker;
34  import io.netty.util.concurrent.Future;
35  import io.netty.util.concurrent.FutureListener;
36  import io.netty.util.concurrent.ImmediateExecutor;
37  import io.netty.util.internal.EmptyArrays;
38  import io.netty.util.internal.StringUtil;
39  import io.netty.util.internal.SystemPropertyUtil;
40  import io.netty.util.internal.UnstableApi;
41  import io.netty.util.internal.logging.InternalLogger;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  
44  import java.security.KeyStore;
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.Arrays;
55  import java.util.Collections;
56  import java.util.LinkedHashSet;
57  import java.util.List;
58  import java.util.Map;
59  import java.util.Set;
60  import java.util.concurrent.ConcurrentHashMap;
61  import java.util.concurrent.ConcurrentMap;
62  import java.util.concurrent.Executor;
63  import java.util.concurrent.locks.Lock;
64  import java.util.concurrent.locks.ReadWriteLock;
65  import java.util.concurrent.locks.ReentrantReadWriteLock;
66  import java.util.function.Function;
67  import javax.net.ssl.KeyManager;
68  import javax.net.ssl.KeyManagerFactory;
69  import javax.net.ssl.SNIServerName;
70  import javax.net.ssl.SSLEngine;
71  import javax.net.ssl.SSLException;
72  import javax.net.ssl.SSLHandshakeException;
73  import javax.net.ssl.TrustManager;
74  import javax.net.ssl.X509ExtendedTrustManager;
75  import javax.net.ssl.X509KeyManager;
76  import javax.net.ssl.X509TrustManager;
77  
78  import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS;
79  import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites;
80  import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
81  import static io.netty.util.internal.ObjectUtil.checkNotNull;
82  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
83  
84  /**
85   * An implementation of {@link SslContext} which works with libraries that support the
86   * <a href="https://www.openssl.org/">OpenSsl</a> C library API.
87   * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
88   *
89   * <p>Instances of this class <strong>must not</strong> be released before any {@link ReferenceCountedOpenSslEngine}
90   * which depends upon the instance of this class is released. Otherwise if any method of
91   * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash.
92   */
93  public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted {
94      private static final InternalLogger logger =
95              InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class);
96  
97      private static final boolean DEFAULT_USE_JDK_PROVIDERS = SystemPropertyUtil.getBoolean(
98              "io.netty.handler.ssl.useJdkProviderSignatures", true);
99      private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE = Math.max(1,
100             SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
101                     2048));
102     // Let's use tasks by default but still allow the user to disable it via system property just in case.
103     static final boolean USE_TASKS =
104             SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", true);
105     private static final Integer DH_KEY_LENGTH;
106     private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
107             ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class);
108 
109     // TODO: Maybe make configurable ?
110     protected static final int VERIFY_DEPTH = 10;
111 
112     static final boolean CLIENT_ENABLE_SESSION_TICKET =
113             SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", false);
114 
115     static final boolean CLIENT_ENABLE_SESSION_TICKET_TLSV13 =
116             SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", true);
117 
118     static final boolean SERVER_ENABLE_SESSION_TICKET =
119             SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", false);
120 
121      static final boolean SERVER_ENABLE_SESSION_TICKET_TLSV13 =
122             SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", true);
123 
124     static final boolean SERVER_ENABLE_SESSION_CACHE =
125             SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheServer", true);
126     static final boolean CLIENT_ENABLE_SESSION_CACHE =
127             SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheClient", true);
128 
129     /**
130      * The OpenSSL SSL_CTX object.
131      *
132      * <strong>{@link #ctxLock} must be hold while using ctx!</strong>
133      */
134     protected long ctx;
135     private final List<String> unmodifiableCiphers;
136     private final OpenSslApplicationProtocolNegotiator apn;
137     private final int mode;
138 
139     // Reference Counting
140     private final ResourceLeakTracker<ReferenceCountedOpenSslContext> leak;
141     private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() {
142         @Override
143         public ReferenceCounted touch(Object hint) {
144             if (leak != null) {
145                 leak.record(hint);
146             }
147 
148             return ReferenceCountedOpenSslContext.this;
149         }
150 
151         @Override
152         protected void deallocate() {
153             try {
154                 destroy();
155             } finally {
156                 if (leak != null) {
157                     boolean closed = leak.close(ReferenceCountedOpenSslContext.this);
158                     assert closed;
159                 }
160             }
161         }
162     };
163 
164     final Certificate[] keyCertChain;
165     final ClientAuth clientAuth;
166     final String[] protocols;
167     final String endpointIdentificationAlgorithm;
168     final List<SNIServerName> serverNames;
169     final boolean hasTLSv13Cipher;
170     final boolean hasTmpDhKeys;
171     final String[] groups;
172     final boolean enableOcsp;
173     final ConcurrentMap<Long, ReferenceCountedOpenSslEngine> engines = new ConcurrentHashMap<>();
174     final ReadWriteLock ctxLock = new ReentrantReadWriteLock();
175     final List<OpenSslCredential> credentials = new ArrayList<>();
176 
177     private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;
178 
179     @SuppressWarnings("deprecation")
180     static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
181             new OpenSslApplicationProtocolNegotiator() {
182                 @Override
183                 public ApplicationProtocolConfig.Protocol protocol() {
184                     return ApplicationProtocolConfig.Protocol.NONE;
185                 }
186 
187                 @Override
188                 public List<String> protocols() {
189                     return Collections.emptyList();
190                 }
191 
192                 @Override
193                 public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
194                     return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
195                 }
196 
197                 @Override
198                 public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
199                     return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
200                 }
201             };
202 
203     static {
204         Integer dhLen = null;
205 
206         try {
207             String dhKeySize = SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize");
208             if (dhKeySize != null) {
209                 try {
210                     dhLen = Integer.valueOf(dhKeySize);
211                 } catch (NumberFormatException e) {
212                     logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: "
213                             + dhKeySize);
214                 }
215             }
216         } catch (Throwable ignore) {
217             // ignore
218         }
219         DH_KEY_LENGTH = dhLen;
220     }
221 
222     final boolean tlsFalseStart;
223 
224     ReferenceCountedOpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
225                                    OpenSslApplicationProtocolNegotiator apn, int mode, Certificate[] keyCertChain,
226                                    ClientAuth clientAuth, String[] protocols, boolean startTls,
227                                    String endpointIdentificationAlgorithm, boolean enableOcsp,
228                                    boolean leakDetection, List<SNIServerName> serverNames,
229                                    ResumptionController resumptionController,
230                                    Map.Entry<SslContextOption<?>, Object>[] ctxOptions,
231                                    List<OpenSslCredential> credentials)
232             throws SSLException {
233         super(startTls, resumptionController);
234 
235         OpenSsl.ensureAvailability();
236 
237         if (enableOcsp && !OpenSsl.isOcspSupported()) {
238             throw new IllegalStateException("OCSP is not supported.");
239         }
240 
241         if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) {
242             throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT");
243         }
244 
245         boolean tlsFalseStart = false;
246         boolean useTasks = USE_TASKS;
247         OpenSslPrivateKeyMethod privateKeyMethod = null;
248         OpenSslAsyncPrivateKeyMethod asyncPrivateKeyMethod = null;
249         OpenSslCertificateCompressionConfig certCompressionConfig = null;
250         Integer maxCertificateList = null;
251         Integer tmpDhKeyLength = null;
252         String[] groups = OpenSsl.NAMED_GROUPS;
253         if (ctxOptions != null) {
254             for (Map.Entry<SslContextOption<?>, Object> ctxOpt : ctxOptions) {
255                 SslContextOption<?> option = ctxOpt.getKey();
256 
257                 if (option == OpenSslContextOption.TLS_FALSE_START) {
258                     tlsFalseStart = (Boolean) ctxOpt.getValue();
259                 } else if (option == OpenSslContextOption.USE_TASKS) {
260                     useTasks = (Boolean) ctxOpt.getValue();
261                 } else if (option == OpenSslContextOption.PRIVATE_KEY_METHOD) {
262                     privateKeyMethod = (OpenSslPrivateKeyMethod) ctxOpt.getValue();
263                 } else if (option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD) {
264                     asyncPrivateKeyMethod = (OpenSslAsyncPrivateKeyMethod) ctxOpt.getValue();
265                 } else if (option == OpenSslContextOption.CERTIFICATE_COMPRESSION_ALGORITHMS) {
266                     certCompressionConfig = (OpenSslCertificateCompressionConfig) ctxOpt.getValue();
267                 } else if (option == OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES) {
268                     maxCertificateList = (Integer) ctxOpt.getValue();
269                 } else if (option == OpenSslContextOption.TMP_DH_KEYLENGTH) {
270                     tmpDhKeyLength = (Integer) ctxOpt.getValue();
271                 } else if (option == OpenSslContextOption.GROUPS) {
272                     String[] groupsArray = (String[]) ctxOpt.getValue();
273                     Set<String> groupsSet = new LinkedHashSet<String>(groupsArray.length);
274                     for (String group : groupsArray) {
275                         groupsSet.add(GroupsConverter.toOpenSsl(group));
276                     }
277                     groups = groupsSet.toArray(EmptyArrays.EMPTY_STRINGS);
278                 } else if (option == OpenSslContextOption.USE_JDK_PROVIDER_SIGNATURES) {
279                     // Alternative key fallback policy - handled during key material setup
280                     logger.debug("Alternative key fallback policy set to: " + ctxOpt.getValue());
281                 } else {
282                     logger.debug("Skipping unsupported " + SslContextOption.class.getSimpleName()
283                             + ": " + ctxOpt.getKey());
284                 }
285             }
286         }
287         if (privateKeyMethod != null && asyncPrivateKeyMethod != null) {
288             throw new IllegalArgumentException("You can either only use "
289                     + OpenSslAsyncPrivateKeyMethod.class.getSimpleName() + " or "
290                     + OpenSslPrivateKeyMethod.class.getSimpleName());
291         }
292 
293         this.tlsFalseStart = tlsFalseStart;
294 
295         leak = leakDetection ? leakDetector.track(this) : null;
296         this.mode = mode;
297         this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
298         this.protocols = protocols == null ? OpenSsl.defaultProtocols(mode == SSL.SSL_MODE_CLIENT) : protocols;
299         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
300         this.serverNames = serverNames;
301         this.enableOcsp = enableOcsp;
302 
303         this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone();
304 
305         String[] suites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
306                 ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites());
307         // Filter out duplicates.
308         LinkedHashSet<String> suitesSet = new LinkedHashSet<String>(suites.length);
309         Collections.addAll(suitesSet, suites);
310         unmodifiableCiphers = new ArrayList<String>(suitesSet);
311 
312         this.apn = checkNotNull(apn, "apn");
313 
314         // Create a new SSL_CTX and configure it.
315         boolean success = false;
316         try {
317             boolean tlsv13Supported = OpenSsl.isTlsv13Supported();
318             boolean anyTlsv13Ciphers = false;
319             try {
320                 int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 |
321                         SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2;
322                 if (tlsv13Supported) {
323                     protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3;
324                 }
325                 ctx = SSLContext.make(protocolOpts, mode);
326             } catch (Exception e) {
327                 throw new SSLException("failed to create an SSL_CTX", e);
328             }
329 
330             StringBuilder cipherBuilder = new StringBuilder();
331             StringBuilder cipherTLSv13Builder = new StringBuilder();
332 
333             /* List the ciphers that are permitted to negotiate. */
334             try {
335                 if (unmodifiableCiphers.isEmpty()) {
336                     // Set non TLSv1.3 ciphers.
337                     SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false);
338                     if (tlsv13Supported) {
339                         // Set TLSv1.3 ciphers.
340                         SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true);
341                     }
342                 } else {
343                     CipherSuiteConverter.convertToCipherStrings(
344                             unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder,
345                             OpenSsl.isBoringSSL());
346 
347                     // Set non TLSv1.3 ciphers.
348                     SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);
349                     if (tlsv13Supported) {
350                         // Set TLSv1.3 ciphers.
351                         String tlsv13Ciphers = OpenSsl.checkTls13Ciphers(logger, cipherTLSv13Builder.toString());
352                         SSLContext.setCipherSuite(ctx, tlsv13Ciphers, true);
353                         if (!tlsv13Ciphers.isEmpty()) {
354                             anyTlsv13Ciphers = true;
355                         }
356                     }
357                 }
358             } catch (SSLException e) {
359                 throw e;
360             } catch (Exception e) {
361                 throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
362             }
363 
364             int options = SSLContext.getOptions(ctx) |
365                     SSL.SSL_OP_NO_SSLv2 |
366                     SSL.SSL_OP_NO_SSLv3 |
367                     // Disable TLSv1 and TLSv1.1 by default as these are not considered secure anymore
368                     // and the JDK is doing the same:
369                     // https://www.oracle.com/java/technologies/javase/8u291-relnotes.html
370                     SSL.SSL_OP_NO_TLSv1 |
371                     SSL.SSL_OP_NO_TLSv1_1 |
372 
373                     SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
374 
375                     // We do not support compression at the moment so we should explicitly disable it.
376                     SSL.SSL_OP_NO_COMPRESSION |
377 
378                     // Disable ticket support by default to be more inline with SSLEngineImpl of the JDK.
379                     // This also let SSLSession.getId() work the same way for the JDK implementation and the
380                     // OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the
381                     // server-side if it could make use of tickets.
382                     SSL.SSL_OP_NO_TICKET;
383 
384             if (cipherBuilder.length() == 0) {
385                 // No ciphers that are compatible with SSLv2 / SSLv3 / TLSv1 / TLSv1.1 / TLSv1.2
386                 options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1
387                         | SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2;
388             }
389 
390             if (!tlsv13Supported) {
391                 // Explicit disable TLSv1.3
392                 // See:
393                 //  - https://github.com/netty/netty/issues/12968
394                 options |= SSL.SSL_OP_NO_TLSv1_3;
395             }
396 
397             hasTLSv13Cipher = anyTlsv13Ciphers;
398             SSLContext.setOptions(ctx, options);
399 
400             // We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between
401             // calling OpenSSLEngine.wrap(...).
402             // See https://github.com/netty/netty-tcnative/issues/100
403             SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
404 
405             if (tmpDhKeyLength != null) {
406                 SSLContext.setTmpDHLength(ctx, tmpDhKeyLength);
407                 hasTmpDhKeys = true;
408             } else if (DH_KEY_LENGTH != null) {
409                 SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH);
410                 hasTmpDhKeys = true;
411             } else {
412                 hasTmpDhKeys = false;
413             }
414 
415             List<String> nextProtoList = apn.protocols();
416             /* Set next protocols for next protocol negotiation extension, if specified */
417             if (!nextProtoList.isEmpty()) {
418                 String[] appProtocols = nextProtoList.toArray(EmptyArrays.EMPTY_STRINGS);
419                 int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
420 
421                 switch (apn.protocol()) {
422                     case NPN:
423                         SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
424                         break;
425                     case ALPN:
426                         SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
427                         break;
428                     case NPN_AND_ALPN:
429                         SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior);
430                         SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior);
431                         break;
432                     default:
433                         throw new Error("Unexpected apn protocol: " + apn.protocol());
434                 }
435             }
436 
437             if (enableOcsp) {
438                 SSLContext.enableOcsp(ctx, isClient());
439             }
440 
441             SSLContext.setUseTasks(ctx, useTasks);
442             if (privateKeyMethod != null) {
443                 SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engines, privateKeyMethod));
444             }
445             if (asyncPrivateKeyMethod != null) {
446                 SSLContext.setPrivateKeyMethod(ctx, new AsyncPrivateKeyMethod(engines, asyncPrivateKeyMethod));
447             }
448             if (certCompressionConfig != null) {
449                 for (OpenSslCertificateCompressionConfig.AlgorithmConfig configPair : certCompressionConfig) {
450                     final CertificateCompressionAlgo algo = new CompressionAlgorithm(engines, configPair.algorithm());
451                     switch (configPair.mode()) {
452                         case Decompress:
453                             SSLContext.addCertificateCompressionAlgorithm(
454                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_DECOMPRESS, algo);
455                             break;
456                         case Compress:
457                             SSLContext.addCertificateCompressionAlgorithm(
458                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_COMPRESS, algo);
459                             break;
460                         case Both:
461                             SSLContext.addCertificateCompressionAlgorithm(
462                                     ctx, SSL.SSL_CERT_COMPRESSION_DIRECTION_BOTH, algo);
463                             break;
464                         default:
465                             throw new IllegalStateException();
466                     }
467                 }
468             }
469             if (maxCertificateList != null) {
470                 SSLContext.setMaxCertList(ctx, maxCertificateList);
471             }
472 
473             // Set the curves / groups if anything is configured.
474             if (groups.length > 0 && !SSLContext.setCurvesList(ctx, groups)) {
475                 String msg = "failed to set curves / groups suite: " + Arrays.toString(groups);
476                 int err = SSL.getLastErrorNumber();
477                 if (err != 0) {
478                     // We have some more details about why the operations failed, include these into the message.
479                     msg += ". " + SSL.getErrorString(err);
480                 }
481                 throw new SSLException(msg);
482             }
483             this.groups = groups;
484 
485             // Add credentials if provided
486             if (credentials != null && !credentials.isEmpty()) {
487                 for (OpenSslCredential credential : credentials) {
488                     addCredential(credential);
489                 }
490             }
491 
492             success = true;
493         } finally {
494             if (!success) {
495                 release();
496             }
497         }
498     }
499 
500     private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) {
501         switch (behavior) {
502             case NO_ADVERTISE:
503                 return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
504             case CHOOSE_MY_LAST_PROTOCOL:
505                 return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
506             default:
507                 throw new Error("Unexpected behavior: " + behavior);
508         }
509     }
510 
511     private void addCredential(OpenSslCredential credential) throws SSLException {
512         if (!(credential instanceof OpenSslCredentialPointer)) {
513             IllegalArgumentException iae = new IllegalArgumentException("Unsupported credential type: " + credential);
514             try {
515                 credential.release();
516             } catch (Throwable th) {
517                 iae.addSuppressed(th);
518             }
519             throw iae;
520         }
521         OpenSslCredentialPointer pointer = (OpenSslCredentialPointer) credential;
522 
523         // Retain the credential for the lifetime of this context
524         // Must be done outside the try block so that if retain() throws,
525         // we don't try to release() and hide the original exception
526         credential.retain();
527         try {
528             credentials.add(credential);
529             SSLContext.addCredential(ctx, pointer.credentialAddress());
530         } catch (Exception e) {
531             credentials.remove(credential);
532             credential.release();
533             throw new SSLException("Failed to add credential to SSL context", e);
534         }
535     }
536 
537     @Override
538     public final List<String> cipherSuites() {
539         return unmodifiableCiphers;
540     }
541 
542     @Override
543     public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
544         return apn;
545     }
546 
547     @Override
548     public final boolean isClient() {
549         return mode == SSL.SSL_MODE_CLIENT;
550     }
551 
552     @Override
553     public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
554         return newEngine0(alloc, peerHost, peerPort, true);
555     }
556 
557     @Override
558     protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
559         return new SslHandler(newEngine0(alloc, null, -1, false), startTls, ImmediateExecutor.INSTANCE,
560                 resumptionController);
561     }
562 
563     @Override
564     protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
565         return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls, ImmediateExecutor.INSTANCE,
566                 resumptionController);
567     }
568 
569     @Override
570     protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) {
571         return new SslHandler(newEngine0(alloc, null, -1, false), startTls, executor, resumptionController);
572     }
573 
574     @Override
575     protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
576                                     boolean startTls, Executor executor) {
577         return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), false, executor, resumptionController);
578     }
579 
580     SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
581         return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true,
582                 endpointIdentificationAlgorithm, serverNames);
583     }
584 
585     /**
586      * Returns a new server-side {@link SSLEngine} with the current configuration.
587      */
588     @Override
589     public final SSLEngine newEngine(ByteBufAllocator alloc) {
590         return newEngine(alloc, null, -1);
591     }
592 
593     /**
594      * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}.
595      * Be aware that it is freed as soon as the {@link #finalize()}  method is called.
596      * At this point {@code 0} will be returned.
597      *
598      * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it!
599      */
600     @Deprecated
601     public final long context() {
602         return sslCtxPointer();
603     }
604 
605     /**
606      * Returns the stats of this context.
607      *
608      * @deprecated use {@link #sessionContext#stats()}
609      */
610     @Deprecated
611     public final OpenSslSessionStats stats() {
612         return sessionContext().stats();
613     }
614 
615     /**
616      * {@deprecated Renegotiation is not supported}
617      * Specify if remote initiated renegotiation is supported or not. If not supported and the remote side tries
618      * to initiate a renegotiation a {@link SSLHandshakeException} will be thrown during decoding.
619      */
620     @Deprecated
621     public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) {
622         if (!rejectRemoteInitiatedRenegotiation) {
623             throw new UnsupportedOperationException("Renegotiation is not supported");
624         }
625     }
626 
627     /**
628      * {@deprecated Renegotiation is not supported}
629      * @return {@code true} because renegotiation is not supported.
630      */
631     @Deprecated
632     public boolean getRejectRemoteInitiatedRenegotiation() {
633         return true;
634     }
635 
636     /**
637      * Set the size of the buffer used by the BIO for non-application based writes
638      * (e.g. handshake, renegotiation, etc...).
639      */
640     public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) {
641         this.bioNonApplicationBufferSize =
642                 checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize");
643     }
644 
645     /**
646      * Returns the size of the buffer used by the BIO for non-application based writes
647      */
648     public int getBioNonApplicationBufferSize() {
649         return bioNonApplicationBufferSize;
650     }
651 
652     /**
653      * Sets the SSL session ticket keys of this context.
654      *
655      * @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
656      */
657     @Deprecated
658     public final void setTicketKeys(byte[] keys) {
659         sessionContext().setTicketKeys(keys);
660     }
661 
662     @Override
663     public abstract OpenSslSessionContext sessionContext();
664 
665     /**
666      * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}.
667      * Be aware that it is freed as soon as the {@link #release()} method is called.
668      * At this point {@code 0} will be returned.
669      *
670      * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it!
671      */
672     @Deprecated
673     public final long sslCtxPointer() {
674         Lock readerLock = ctxLock.readLock();
675         readerLock.lock();
676         try {
677             return SSLContext.getSslCtx(ctx);
678         } finally {
679             readerLock.unlock();
680         }
681     }
682 
683     /**
684      * Set the {@link OpenSslPrivateKeyMethod} to use. This allows to offload private-key operations
685      * if needed.
686      *
687      * This method is currently only supported when {@code BoringSSL} is used.
688      *
689      * @param        method method to use.
690      * @deprecated   use {@link SslContextBuilder#option(SslContextOption, Object)} with
691      *              {@link OpenSslContextOption#PRIVATE_KEY_METHOD}.
692      */
693     @Deprecated
694     @UnstableApi
695     public final void setPrivateKeyMethod(OpenSslPrivateKeyMethod method) {
696         checkNotNull(method, "method");
697         Lock writerLock = ctxLock.writeLock();
698         writerLock.lock();
699         try {
700             SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engines, method));
701         } finally {
702             writerLock.unlock();
703         }
704     }
705 
706     /**
707      * @deprecated   use {@link SslContextBuilder#option(SslContextOption, Object)} with
708      *              {@link OpenSslContextOption#USE_TASKS}.
709      */
710     @Deprecated
711     public final void setUseTasks(boolean useTasks) {
712         Lock writerLock = ctxLock.writeLock();
713         writerLock.lock();
714         try {
715             SSLContext.setUseTasks(ctx, useTasks);
716         } finally {
717             writerLock.unlock();
718         }
719     }
720 
721     // IMPORTANT: This method must only be called from either the constructor or the finalizer as a user MUST never
722     //            get access to an OpenSslSessionContext after this method was called to prevent the user from
723     //            producing a segfault.
724     private void destroy() {
725         Lock writerLock = ctxLock.writeLock();
726         writerLock.lock();
727         try {
728             if (ctx != 0) {
729                 if (enableOcsp) {
730                     SSLContext.disableOcsp(ctx);
731                 }
732 
733                 SSLContext.free(ctx);
734                 ctx = 0;
735 
736                 OpenSslSessionContext context = sessionContext();
737                 if (context != null) {
738                     context.destroy();
739                 }
740                 for (OpenSslCredential credential : credentials) {
741                     credential.release();
742                 }
743                 credentials.clear();
744             }
745         } finally {
746             writerLock.unlock();
747         }
748     }
749 
750     protected static X509Certificate[] certificates(byte[][] chain) {
751         X509Certificate[] peerCerts = new X509Certificate[chain.length];
752         for (int i = 0; i < peerCerts.length; i++) {
753             peerCerts[i] = new LazyX509Certificate(chain[i]);
754         }
755         return peerCerts;
756     }
757 
758     /**
759      * @deprecated This method is kept for API backwards compatibility.
760      */
761     @Deprecated
762     protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
763         return chooseTrustManager(managers, null);
764     }
765 
766     static X509TrustManager chooseTrustManager(TrustManager[] managers,
767                                                          ResumptionController resumptionController) {
768         for (TrustManager m : managers) {
769             if (m instanceof X509TrustManager) {
770                 X509TrustManager tm = (X509TrustManager) m;
771                 if (resumptionController != null) {
772                     tm = (X509TrustManager) resumptionController.wrapIfNeeded(tm);
773                 }
774                 tm = OpenSslX509TrustManagerWrapper.wrapIfNeeded((X509TrustManager) m);
775                 if (useExtendedTrustManager(tm)) {
776                     // Wrap the TrustManager to provide a better exception message for users to debug hostname
777                     // validation failures.
778                     tm = new EnhancingX509ExtendedTrustManager(tm);
779                 }
780                 return tm;
781             }
782         }
783         throw new IllegalStateException("no X509TrustManager found");
784     }
785 
786     protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) {
787         for (KeyManager km : kms) {
788             if (km instanceof X509KeyManager) {
789                 return (X509KeyManager) km;
790             }
791         }
792         throw new IllegalStateException("no X509KeyManager found");
793     }
794 
795     /**
796      * Translate a {@link ApplicationProtocolConfig} object to a
797      * {@link OpenSslApplicationProtocolNegotiator} object.
798      *
799      * @param config The configuration which defines the translation
800      * @return The results of the translation
801      */
802     @SuppressWarnings("deprecation")
803     static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) {
804         if (config == null) {
805             return NONE_PROTOCOL_NEGOTIATOR;
806         }
807 
808         switch (config.protocol()) {
809             case NONE:
810                 return NONE_PROTOCOL_NEGOTIATOR;
811             case ALPN:
812             case NPN:
813             case NPN_AND_ALPN:
814                 switch (config.selectedListenerFailureBehavior()) {
815                     case CHOOSE_MY_LAST_PROTOCOL:
816                     case ACCEPT:
817                         switch (config.selectorFailureBehavior()) {
818                             case CHOOSE_MY_LAST_PROTOCOL:
819                             case NO_ADVERTISE:
820                                 return new OpenSslDefaultApplicationProtocolNegotiator(
821                                         config);
822                             default:
823                                 throw new UnsupportedOperationException(
824                                         "OpenSSL provider does not support " +
825                                                 config.selectorFailureBehavior() +
826                                                 " behavior");
827                         }
828                     default:
829                         throw new UnsupportedOperationException(
830                                 "OpenSSL provider does not support " +
831                                         config.selectedListenerFailureBehavior() +
832                                         " behavior");
833                 }
834             default:
835                 throw new Error("Unexpected protocol: " + config.protocol());
836         }
837     }
838 
839     static boolean useExtendedTrustManager(X509TrustManager trustManager) {
840         return trustManager instanceof X509ExtendedTrustManager;
841     }
842 
843     @Override
844     public final int refCnt() {
845         return refCnt.refCnt();
846     }
847 
848     @Override
849     public final ReferenceCounted retain() {
850         refCnt.retain();
851         return this;
852     }
853 
854     @Override
855     public final ReferenceCounted retain(int increment) {
856         refCnt.retain(increment);
857         return this;
858     }
859 
860     @Override
861     public final ReferenceCounted touch() {
862         refCnt.touch();
863         return this;
864     }
865 
866     @Override
867     public final ReferenceCounted touch(Object hint) {
868         refCnt.touch(hint);
869         return this;
870     }
871 
872     @Override
873     public final boolean release() {
874         return refCnt.release();
875     }
876 
877     @Override
878     public final boolean release(int decrement) {
879         return refCnt.release(decrement);
880     }
881 
882     abstract static class AbstractCertificateVerifier extends CertificateVerifier {
883         private final Map<Long, ReferenceCountedOpenSslEngine> engines;
884 
885         AbstractCertificateVerifier(Map<Long, ReferenceCountedOpenSslEngine> engines) {
886             this.engines = engines;
887         }
888 
889         @Override
890         public final int verify(long ssl, byte[][] chain, String auth) {
891             final ReferenceCountedOpenSslEngine engine = engines.get(ssl);
892             if (engine == null) {
893                 // May be null if it was destroyed in the meantime.
894                 return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
895             }
896             X509Certificate[] peerCerts = certificates(chain);
897             try {
898                 verify(engine, peerCerts, auth);
899                 return CertificateVerifier.X509_V_OK;
900             } catch (Throwable cause) {
901                 logger.debug("verification of certificate failed", cause);
902                 engine.initHandshakeException(cause);
903 
904                 // Try to extract the correct error code that should be used.
905                 if (cause instanceof OpenSslCertificateException) {
906                     // This will never return a negative error code as its validated when constructing the
907                     // OpenSslCertificateException.
908                     return ((OpenSslCertificateException) cause).errorCode();
909                 }
910                 if (cause instanceof CertificateExpiredException) {
911                     return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
912                 }
913                 if (cause instanceof CertificateNotYetValidException) {
914                     return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
915                 }
916                 return translateToError(cause);
917             }
918         }
919 
920         private static int translateToError(Throwable cause) {
921             if (cause instanceof CertificateRevokedException) {
922                 return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
923             }
924 
925             // The X509TrustManagerImpl uses a Validator which wraps a CertPathValidatorException into
926             // an CertificateException. So we need to handle the wrapped CertPathValidatorException to be
927             // able to send the correct alert.
928             Throwable wrapped = cause.getCause();
929             while (wrapped != null) {
930                 if (wrapped instanceof CertPathValidatorException) {
931                     CertPathValidatorException ex = (CertPathValidatorException) wrapped;
932                     CertPathValidatorException.Reason reason = ex.getReason();
933                     if (reason == CertPathValidatorException.BasicReason.EXPIRED) {
934                         return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED;
935                     }
936                     if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
937                         return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID;
938                     }
939                     if (reason == CertPathValidatorException.BasicReason.REVOKED) {
940                         return CertificateVerifier.X509_V_ERR_CERT_REVOKED;
941                     }
942                 }
943                 wrapped = wrapped.getCause();
944             }
945             return CertificateVerifier.X509_V_ERR_UNSPECIFIED;
946         }
947 
948         abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts,
949                              String auth) throws Exception;
950     }
951 
952     static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
953             throws SSLException {
954          /* Load the certificate file and private key. */
955         long keyBio = 0;
956         long keyCertChainBio = 0;
957         long keyCertChainBio2 = 0;
958         PemEncoded encoded = null;
959         try {
960             // Only encode one time
961             encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain);
962             keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
963             keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
964 
965             if (key != null) {
966                 keyBio = toBIO(ByteBufAllocator.DEFAULT, key);
967             }
968 
969             SSLContext.setCertificateBio(
970                     ctx, keyCertChainBio, keyBio,
971                     keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword);
972             // We may have more then one cert in the chain so add all of them now.
973             SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true);
974         } catch (SSLException e) {
975             throw e;
976         } catch (Exception e) {
977             throw new SSLException("failed to set certificate and key", e);
978         } finally {
979             freeBio(keyBio);
980             freeBio(keyCertChainBio);
981             freeBio(keyCertChainBio2);
982             if (encoded != null) {
983                 encoded.release();
984             }
985         }
986     }
987 
988     /**
989      * Check if JDK signature fallback is enabled in the given context options.
990      */
991     @SafeVarargs
992     static boolean isJdkSignatureFallbackEnabled(Map.Entry<SslContextOption<?>, Object>... ctxOptions) {
993         boolean allowJdkFallback = DEFAULT_USE_JDK_PROVIDERS;
994         for (Map.Entry<SslContextOption<?>, Object> entry : ctxOptions) {
995             SslContextOption<?> option = entry.getKey();
996             if (option == OpenSslContextOption.USE_JDK_PROVIDER_SIGNATURES) {
997                 Boolean policy = (Boolean) entry.getValue();
998                 allowJdkFallback = policy.booleanValue();
999             } else if (option == OpenSslContextOption.PRIVATE_KEY_METHOD ||
1000                        option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD) {
1001                 // if the user has set a private key method already we don't want to support
1002                 // fallback.
1003                 return false;
1004             }
1005         }
1006         return allowJdkFallback; // Default policy
1007     }
1008 
1009     static void freeBio(long bio) {
1010         if (bio != 0) {
1011             SSL.freeBIO(bio);
1012         }
1013     }
1014 
1015     /**
1016      * Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
1017      * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}.
1018      */
1019     static long toBIO(ByteBufAllocator allocator, PrivateKey key) throws Exception {
1020         if (key == null) {
1021             return 0;
1022         }
1023 
1024         PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key);
1025         try {
1026             return toBIO(allocator, pem.retain());
1027         } finally {
1028             pem.release();
1029         }
1030     }
1031 
1032     /**
1033      * Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
1034      * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}.
1035      */
1036     static long toBIO(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception {
1037         if (certChain == null) {
1038             return 0;
1039         }
1040 
1041         checkNonEmpty(certChain, "certChain");
1042 
1043         PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain);
1044         try {
1045             return toBIO(allocator, pem.retain());
1046         } finally {
1047             pem.release();
1048         }
1049     }
1050 
1051     static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception {
1052         try {
1053             // We can turn direct buffers straight into BIOs. No need to
1054             // make a yet another copy.
1055             ByteBuf content = pem.content();
1056 
1057             if (content.isDirect()) {
1058                 return newBIO(content.retainedSlice());
1059             }
1060 
1061             ByteBuf buffer = allocator.directBuffer(content.readableBytes());
1062             try {
1063                 buffer.writeBytes(content, content.readerIndex(), content.readableBytes());
1064                 return newBIO(buffer.retainedSlice());
1065             } finally {
1066                 try {
1067                     // If the contents of the ByteBuf is sensitive (e.g. a PrivateKey) we
1068                     // need to zero out the bytes of the copy before we're releasing it.
1069                     if (pem.isSensitive()) {
1070                         SslUtils.zeroout(buffer);
1071                     }
1072                 } finally {
1073                     buffer.release();
1074                 }
1075             }
1076         } finally {
1077             pem.release();
1078         }
1079     }
1080 
1081     private static long newBIO(ByteBuf buffer) throws Exception {
1082         try {
1083             long bio = SSL.newMemBIO();
1084             int readable = buffer.readableBytes();
1085             if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
1086                 SSL.freeBIO(bio);
1087                 throw new IllegalStateException("Could not write data to memory BIO");
1088             }
1089             return bio;
1090         } finally {
1091             buffer.release();
1092         }
1093     }
1094 
1095     /**
1096      * Returns the {@link OpenSslKeyMaterialProvider} that should be used for OpenSSL. Depending on the given
1097      * {@link KeyManagerFactory} this may cache the {@link OpenSslKeyMaterial} for better performance if it can
1098      * ensure that the same material is always returned for the same alias.
1099      */
1100     static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) {
1101         if (factory instanceof OpenSslX509KeyManagerFactory) {
1102             return ((OpenSslX509KeyManagerFactory) factory).newProvider();
1103         }
1104 
1105         if (factory instanceof OpenSslCachingX509KeyManagerFactory) {
1106             // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache.
1107             return ((OpenSslCachingX509KeyManagerFactory) factory).newProvider(password);
1108         }
1109         // We can not be sure if the material may change at runtime so we will not cache it.
1110         return new OpenSslKeyMaterialProvider(chooseX509KeyManager(factory.getKeyManagers()), password);
1111     }
1112 
1113     static KeyManagerFactory certChainToKeyManagerFactory(X509Certificate[] keyCertChain, PrivateKey key,
1114                                                           String keyPassword, String keyStore) throws Exception {
1115         KeyManagerFactory keyManagerFactory;
1116         char[] keyPasswordChars = keyStorePassword(keyPassword);
1117         KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore);
1118         if (ks.aliases().hasMoreElements()) {
1119             keyManagerFactory = new OpenSslX509KeyManagerFactory();
1120         } else {
1121             keyManagerFactory = new OpenSslCachingX509KeyManagerFactory(
1122                     KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()));
1123         }
1124         keyManagerFactory.init(ks, keyPasswordChars);
1125         return keyManagerFactory;
1126     }
1127 
1128     static OpenSslKeyMaterialProvider setupSecurityProviderSignatureSource(
1129             ReferenceCountedOpenSslContext thiz, long ctx, X509Certificate[] keyCertChain, PrivateKey key,
1130             Function<OpenSslKeyMaterialManager, CertificateCallback> toCallback) throws Exception {
1131         // 1. Set up the async private key method for signing operations
1132         SSLContext.setPrivateKeyMethod(ctx, new JdkDelegatingPrivateKeyMethod(key));
1133 
1134         // 2. Set up keyless KeyManagerFactory and certificate callback for certificate provision
1135         KeyManagerFactory keylessKmf = OpenSslX509KeyManagerFactory.newKeyless(keyCertChain);
1136         OpenSslKeyMaterialProvider keyMaterialProvider = providerFor(keylessKmf, "");
1137         try {
1138             // Set up certificate callback for alternative keys - required for client certificates
1139             OpenSslKeyMaterialManager materialManager =
1140                     new OpenSslKeyMaterialManager(keyMaterialProvider, thiz.hasTmpDhKeys);
1141             SSLContext.setCertificateCallback(ctx, toCallback.apply(materialManager));
1142             return keyMaterialProvider;
1143         } catch (Throwable cause) {
1144             // Destroy the provider in case of failure as otherwise we might leak memory.
1145             keyMaterialProvider.destroy();
1146             throw cause;
1147         }
1148     }
1149 
1150     private static ReferenceCountedOpenSslEngine retrieveEngine(Map<Long, ReferenceCountedOpenSslEngine> engines,
1151                                                                 long ssl)
1152             throws SSLException {
1153         ReferenceCountedOpenSslEngine engine = engines.get(ssl);
1154         if (engine == null) {
1155             throw new SSLException("Could not find a " +
1156                     StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl);
1157         }
1158         return engine;
1159     }
1160 
1161     private static final class PrivateKeyMethod implements SSLPrivateKeyMethod {
1162 
1163         private final Map<Long, ReferenceCountedOpenSslEngine> engines;
1164         private final OpenSslPrivateKeyMethod keyMethod;
1165         PrivateKeyMethod(Map<Long, ReferenceCountedOpenSslEngine> engines, OpenSslPrivateKeyMethod keyMethod) {
1166             this.engines = engines;
1167             this.keyMethod = keyMethod;
1168         }
1169 
1170         @Override
1171         public byte[] sign(long ssl, int signatureAlgorithm, byte[] digest) throws Exception {
1172             ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1173             try {
1174                 return verifyResult(keyMethod.sign(engine, signatureAlgorithm, digest));
1175             } catch (Exception e) {
1176                 engine.initHandshakeException(e);
1177                 throw e;
1178             }
1179         }
1180 
1181         @Override
1182         public byte[] decrypt(long ssl, byte[] input) throws Exception {
1183             ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1184             try {
1185                 return verifyResult(keyMethod.decrypt(engine, input));
1186             } catch (Exception e) {
1187                 engine.initHandshakeException(e);
1188                 throw e;
1189             }
1190         }
1191     }
1192 
1193     private static final class AsyncPrivateKeyMethod implements AsyncSSLPrivateKeyMethod {
1194 
1195         private final Map<Long, ReferenceCountedOpenSslEngine> engines;
1196         private final OpenSslAsyncPrivateKeyMethod keyMethod;
1197 
1198         AsyncPrivateKeyMethod(Map<Long, ReferenceCountedOpenSslEngine> engines,
1199                               OpenSslAsyncPrivateKeyMethod keyMethod) {
1200             this.engines = engines;
1201             this.keyMethod = keyMethod;
1202         }
1203 
1204         @Override
1205         public void sign(long ssl, int signatureAlgorithm, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1206             try {
1207                 ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1208                 keyMethod.sign(engine, signatureAlgorithm, bytes)
1209                         .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1210             } catch (SSLException e) {
1211                 resultCallback.onError(ssl, e);
1212             }
1213         }
1214 
1215         @Override
1216         public void decrypt(long ssl, byte[] bytes, ResultCallback<byte[]> resultCallback) {
1217             try {
1218                 ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1219                 keyMethod.decrypt(engine, bytes)
1220                         .addListener(new ResultCallbackListener(engine, ssl, resultCallback));
1221             } catch (SSLException e) {
1222                 resultCallback.onError(ssl, e);
1223             }
1224         }
1225 
1226         private static final class ResultCallbackListener implements FutureListener<byte[]> {
1227             private final ReferenceCountedOpenSslEngine engine;
1228             private final long ssl;
1229             private final ResultCallback<byte[]> resultCallback;
1230 
1231             ResultCallbackListener(ReferenceCountedOpenSslEngine engine, long ssl,
1232                                    ResultCallback<byte[]> resultCallback) {
1233                 this.engine = engine;
1234                 this.ssl = ssl;
1235                 this.resultCallback = resultCallback;
1236             }
1237 
1238             @Override
1239             public void operationComplete(Future<byte[]> future) {
1240                 Throwable cause = future.cause();
1241                 if (cause == null) {
1242                     try {
1243                         byte[] result = verifyResult(future.getNow());
1244                         resultCallback.onSuccess(ssl, result);
1245                         return;
1246                     } catch (SignatureException e) {
1247                         cause = e;
1248                         engine.initHandshakeException(e);
1249                     }
1250                 }
1251                 resultCallback.onError(ssl, cause);
1252             }
1253         }
1254     }
1255 
1256     private static byte[] verifyResult(byte[] result) throws SignatureException {
1257         if (result == null) {
1258             throw new SignatureException();
1259         }
1260         return result;
1261     }
1262 
1263     private static final class CompressionAlgorithm implements CertificateCompressionAlgo {
1264         private final Map<Long, ReferenceCountedOpenSslEngine> engines;
1265         private final OpenSslCertificateCompressionAlgorithm compressionAlgorithm;
1266 
1267         CompressionAlgorithm(Map<Long, ReferenceCountedOpenSslEngine> engines,
1268                              OpenSslCertificateCompressionAlgorithm compressionAlgorithm) {
1269             this.engines = engines;
1270             this.compressionAlgorithm = compressionAlgorithm;
1271         }
1272 
1273         @Override
1274         public byte[] compress(long ssl, byte[] bytes) throws Exception {
1275             ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1276             return compressionAlgorithm.compress(engine, bytes);
1277         }
1278 
1279         @Override
1280         public byte[] decompress(long ssl, int len, byte[] bytes) throws Exception {
1281             ReferenceCountedOpenSslEngine engine = retrieveEngine(engines, ssl);
1282             return compressionAlgorithm.decompress(engine, len, bytes);
1283         }
1284 
1285         @Override
1286         public int algorithmId() {
1287             return compressionAlgorithm.algorithmId();
1288         }
1289     }
1290 }