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