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