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