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