View Javadoc
1   /*
2    * Copyright 2014 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  
17  package io.netty.handler.ssl;
18  
19  import io.netty.buffer.ByteBuf;
20  import io.netty.buffer.ByteBufAllocator;
21  import io.netty.buffer.UnpooledByteBufAllocator;
22  import io.netty.internal.tcnative.Buffer;
23  import io.netty.internal.tcnative.Library;
24  import io.netty.internal.tcnative.SSL;
25  import io.netty.internal.tcnative.SSLContext;
26  import io.netty.util.CharsetUtil;
27  import io.netty.util.ReferenceCountUtil;
28  import io.netty.util.ReferenceCounted;
29  import io.netty.util.internal.EmptyArrays;
30  import io.netty.util.internal.NativeLibraryLoader;
31  import io.netty.util.internal.PlatformDependent;
32  import io.netty.util.internal.StringUtil;
33  import io.netty.util.internal.SystemPropertyUtil;
34  import io.netty.util.internal.logging.InternalLogger;
35  import io.netty.util.internal.logging.InternalLoggerFactory;
36  
37  import java.io.ByteArrayInputStream;
38  import java.nio.ByteBuffer;
39  import java.security.cert.CertificateException;
40  import java.security.cert.X509Certificate;
41  import java.util.ArrayList;
42  import java.util.Arrays;
43  import java.util.Collection;
44  import java.util.Collections;
45  import java.util.HashSet;
46  import java.util.Iterator;
47  import java.util.LinkedHashSet;
48  import java.util.List;
49  import java.util.Set;
50  
51  import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
52  import static io.netty.handler.ssl.SslUtils.PROBING_CERT;
53  import static io.netty.handler.ssl.SslUtils.PROBING_KEY;
54  import static io.netty.handler.ssl.SslUtils.TLSV13_CIPHERS;
55  import static io.netty.handler.ssl.SslUtils.TLSV13_CIPHER_SUITES;
56  import static io.netty.handler.ssl.SslUtils.addIfSupported;
57  import static io.netty.handler.ssl.SslUtils.isTLSv13Cipher;
58  import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
59  
60  /**
61   * Tells if <a href="https://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
62   * are available.
63   */
64  public final class OpenSsl {
65  
66      private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class);
67      private static final Throwable UNAVAILABILITY_CAUSE;
68      static final List<String> DEFAULT_CIPHERS;
69      static final Set<String> AVAILABLE_CIPHER_SUITES;
70      private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES;
71      private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES;
72      private static final boolean SUPPORTS_KEYMANAGER_FACTORY;
73      private static final boolean USE_KEYMANAGER_FACTORY;
74      private static final boolean SUPPORTS_OCSP;
75      private static final boolean TLSV13_SUPPORTED;
76      private static final boolean IS_BORINGSSL;
77      private static final boolean IS_AWSLC;
78      private static final Set<String> CLIENT_DEFAULT_PROTOCOLS;
79      private static final Set<String> SERVER_DEFAULT_PROTOCOLS;
80  
81      private static final int SSL_V2_HELLO = 1;
82      private static final int SSL_V2 = 1 << 1;
83      private static final int SSL_V3 = 1 << 2;
84      private static final int TLS_V1 = 1 << 3;
85      private static final int TLS_V1_1 = 1 << 4;
86      private static final int TLS_V1_2 = 1 << 5;
87      private static final int TLS_V1_3 = 1 << 6;
88      private static final int supportedProtocolsPacked;
89  
90      static final String[] EXTRA_SUPPORTED_TLS_1_3_CIPHERS;
91      static final String EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING;
92      static final String[] NAMED_GROUPS;
93  
94      static final boolean JAVAX_CERTIFICATE_CREATION_SUPPORTED;
95  
96      // Use default that is supported in java 11 and earlier and also in OpenSSL / BoringSSL.
97      // See https://github.com/netty/netty-tcnative/issues/567
98      // See https://www.java.com/en/configure_crypto.html for ordering
99      private static final String[] DEFAULT_NAMED_GROUPS = { "x25519", "secp256r1", "secp384r1", "secp521r1" };
100 
101     static {
102         Throwable cause = null;
103 
104         if (SystemPropertyUtil.getBoolean("io.netty.handler.ssl.noOpenSsl", false)) {
105             cause = new UnsupportedOperationException(
106                     "OpenSSL was explicit disabled with -Dio.netty.handler.ssl.noOpenSsl=true");
107 
108             logger.debug(
109                     "netty-tcnative explicit disabled; " +
110                             OpenSslEngine.class.getSimpleName() + " will be unavailable.", cause);
111         } else {
112             // Test if netty-tcnative is in the classpath first.
113             try {
114                 Class.forName("io.netty.internal.tcnative.SSLContext", false,
115                         PlatformDependent.getClassLoader(OpenSsl.class));
116             } catch (ClassNotFoundException t) {
117                 cause = t;
118                 logger.debug(
119                         "netty-tcnative not in the classpath; " +
120                                 OpenSslEngine.class.getSimpleName() + " will be unavailable.");
121             }
122 
123             // If in the classpath, try to load the native library and initialize netty-tcnative.
124             if (cause == null) {
125                 try {
126                     // The JNI library was not already loaded. Load it now.
127                     loadTcNative();
128                 } catch (Throwable t) {
129                     cause = t;
130                     logger.debug(
131                             "Failed to load netty-tcnative; " +
132                                     OpenSslEngine.class.getSimpleName() + " will be unavailable, unless the " +
133                                     "application has already loaded the symbols by some other means. " +
134                                     "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t);
135                 }
136 
137                 try {
138                     String engine = SystemPropertyUtil.get("io.netty.handler.ssl.openssl.engine", null);
139                     if (engine == null) {
140                         logger.debug("Initialize netty-tcnative using engine: 'default'");
141                     } else {
142                         logger.debug("Initialize netty-tcnative using engine: '{}'", engine);
143                     }
144                     initializeTcNative(engine);
145 
146                     // The library was initialized successfully. If loading the library failed above,
147                     // reset the cause now since it appears that the library was loaded by some other
148                     // means.
149                     cause = null;
150                 } catch (Throwable t) {
151                     if (cause == null) {
152                         cause = t;
153                     }
154                     logger.debug(
155                             "Failed to initialize netty-tcnative; " +
156                                     OpenSslEngine.class.getSimpleName() + " will be unavailable. " +
157                                     "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t);
158                 }
159             }
160         }
161 
162         UNAVAILABILITY_CAUSE = cause;
163         CLIENT_DEFAULT_PROTOCOLS = defaultProtocols("jdk.tls.client.protocols");
164         SERVER_DEFAULT_PROTOCOLS = defaultProtocols("jdk.tls.server.protocols");
165 
166         if (cause == null) {
167             logger.debug("netty-tcnative using native library: {}", SSL.versionString());
168 
169             final List<String> defaultCiphers = new ArrayList<>();
170             final Set<String> availableOpenSslCipherSuites = new LinkedHashSet<>(128);
171             boolean supportsKeyManagerFactory = false;
172             boolean useKeyManagerFactory = false;
173             boolean tlsv13Supported = false;
174             String[] namedGroups = DEFAULT_NAMED_GROUPS;
175             Set<String> defaultConvertedNamedGroups = new LinkedHashSet<>(namedGroups.length);
176             for (String group : namedGroups) {
177                 defaultConvertedNamedGroups.add(GroupsConverter.toOpenSsl(group));
178             }
179 
180             IS_BORINGSSL = "BoringSSL".equals(versionString());
181             IS_AWSLC = versionString().startsWith("AWS-LC");
182             if (IS_BORINGSSL) {
183                 EXTRA_SUPPORTED_TLS_1_3_CIPHERS = new String [] { "TLS_AES_128_GCM_SHA256",
184                         "TLS_AES_256_GCM_SHA384" ,
185                         "TLS_CHACHA20_POLY1305_SHA256" };
186 
187                 StringBuilder ciphersBuilder = new StringBuilder(128);
188                 for (String cipher: EXTRA_SUPPORTED_TLS_1_3_CIPHERS) {
189                     ciphersBuilder.append(cipher).append(":");
190                 }
191                 ciphersBuilder.setLength(ciphersBuilder.length() - 1);
192                 EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = ciphersBuilder.toString();
193             }  else {
194                 EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS;
195                 EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = StringUtil.EMPTY_STRING;
196             }
197 
198             try {
199                 final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
200 
201                 // Let's filter out any group that is not supported from the default.
202                 Iterator<String> defaultGroupsIter = defaultConvertedNamedGroups.iterator();
203                 while (defaultGroupsIter.hasNext()) {
204                     if (!SSLContext.setCurvesList(sslCtx, defaultGroupsIter.next())) {
205                         // Not supported, let's remove it. This could for example be the case if we use
206                         // fips and the configure group is not supported when using FIPS.
207                         // See https://github.com/netty/netty-tcnative/issues/883
208                         defaultGroupsIter.remove();
209 
210                         // Clear the error as otherwise we might fail later.
211                         SSL.clearError();
212                     }
213                 }
214                 namedGroups = defaultConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
215 
216                 long certBio = 0;
217                 long keyBio = 0;
218                 long cert = 0;
219                 long key = 0;
220                 try {
221                     // As we delegate to the KeyManager / TrustManager of the JDK we need to ensure it can actually
222                     // handle TLSv13 as otherwise we may see runtime exceptions
223                     if (SslProvider.isTlsv13Supported(SslProvider.JDK)) {
224                         try {
225                             StringBuilder tlsv13Ciphers = new StringBuilder();
226 
227                             for (String cipher : TLSV13_CIPHERS) {
228                                 String converted = CipherSuiteConverter.toOpenSsl(cipher, IS_BORINGSSL);
229                                 if (converted != null) {
230                                     tlsv13Ciphers.append(converted).append(':');
231                                 }
232                             }
233                             if (tlsv13Ciphers.length() == 0) {
234                                 tlsv13Supported = false;
235                             } else {
236                                 tlsv13Ciphers.setLength(tlsv13Ciphers.length() - 1);
237                                 SSLContext.setCipherSuite(sslCtx, tlsv13Ciphers.toString(), true);
238                                 tlsv13Supported = true;
239                             }
240 
241                         } catch (Exception ignore) {
242                             tlsv13Supported = false;
243                             SSL.clearError();
244                         }
245                     }
246 
247                     SSLContext.setCipherSuite(sslCtx, "ALL", false);
248 
249                     final long ssl = SSL.newSSL(sslCtx, true);
250                     try {
251                         for (String c: SSL.getCiphers(ssl)) {
252                             // Filter out bad input.
253                             if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c) ||
254                                 // Filter out TLSv1.3 ciphers if not supported.
255                                 !tlsv13Supported && isTLSv13Cipher(c)) {
256                                 continue;
257                             }
258                             availableOpenSslCipherSuites.add(c);
259                         }
260                         if (IS_BORINGSSL) {
261                             // Currently BoringSSL does not include these when calling SSL.getCiphers() even when these
262                             // are supported.
263                             Collections.addAll(availableOpenSslCipherSuites, EXTRA_SUPPORTED_TLS_1_3_CIPHERS);
264                             Collections.addAll(availableOpenSslCipherSuites,
265                                                "AEAD-AES128-GCM-SHA256",
266                                                "AEAD-AES256-GCM-SHA384",
267                                                "AEAD-CHACHA20-POLY1305-SHA256");
268                         }
269 
270                         PemEncoded privateKey = PemPrivateKey.valueOf(PROBING_KEY.getBytes(CharsetUtil.US_ASCII));
271                         try {
272                             // Let's check if we can set a callback, which may not work if the used OpenSSL version
273                             // is to old.
274                             SSLContext.setCertificateCallback(sslCtx, null);
275 
276                             X509Certificate certificate = selfSignedCertificate();
277                             certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, certificate);
278                             cert = SSL.parseX509Chain(certBio);
279 
280                             keyBio = ReferenceCountedOpenSslContext.toBIO(
281                                     UnpooledByteBufAllocator.DEFAULT, privateKey.retain());
282                             key = SSL.parsePrivateKey(keyBio, null);
283 
284                             SSL.setKeyMaterial(ssl, cert, key);
285                             supportsKeyManagerFactory = true;
286                             try {
287                                 boolean propertySet = SystemPropertyUtil.contains(
288                                         "io.netty.handler.ssl.openssl.useKeyManagerFactory");
289                                 if (!(IS_BORINGSSL || IS_AWSLC)) {
290                                     useKeyManagerFactory = SystemPropertyUtil.getBoolean(
291                                             "io.netty.handler.ssl.openssl.useKeyManagerFactory", true);
292 
293                                     if (propertySet) {
294                                         logger.info("System property " +
295                                                 "'io.netty.handler.ssl.openssl.useKeyManagerFactory'" +
296                                                 " is deprecated and so will be ignored in the future");
297                                     }
298                                 } else {
299                                     useKeyManagerFactory = true;
300                                     if (propertySet) {
301                                         logger.info("System property " +
302                                                 "'io.netty.handler.ssl.openssl.useKeyManagerFactory'" +
303                                                 " is deprecated and will be ignored when using BoringSSL or AWS-LC");
304                                     }
305                                 }
306                             } catch (Throwable ignore) {
307                                 logger.debug("Failed to get useKeyManagerFactory system property.");
308                             }
309                         } catch (Exception e) {
310                             logger.debug("KeyManagerFactory not supported", e);
311                             SSL.clearError();
312                         } finally {
313                             privateKey.release();
314                         }
315                     } finally {
316                         SSL.freeSSL(ssl);
317                         if (certBio != 0) {
318                             SSL.freeBIO(certBio);
319                         }
320                         if (keyBio != 0) {
321                             SSL.freeBIO(keyBio);
322                         }
323                         if (cert != 0) {
324                             SSL.freeX509Chain(cert);
325                         }
326                         if (key != 0) {
327                             SSL.freePrivateKey(key);
328                         }
329                     }
330 
331                     String groups = SystemPropertyUtil.get("jdk.tls.namedGroups", null);
332                     if (groups != null) {
333                         String[] nGroups = groups.split(",");
334                         Set<String> supportedNamedGroups = new LinkedHashSet<>(nGroups.length);
335                         Set<String> supportedConvertedNamedGroups = new LinkedHashSet<>(nGroups.length);
336 
337                         Set<String> unsupportedNamedGroups = new LinkedHashSet<>();
338                         for (String namedGroup : nGroups) {
339                             String converted = GroupsConverter.toOpenSsl(namedGroup);
340                             if (SSLContext.setCurvesList(sslCtx, converted)) {
341                                 supportedConvertedNamedGroups.add(converted);
342                                 supportedNamedGroups.add(namedGroup);
343                             } else {
344                                 unsupportedNamedGroups.add(namedGroup);
345 
346                                 // Clear the error as otherwise we might fail later.
347                                 SSL.clearError();
348                             }
349                         }
350 
351                         if (supportedNamedGroups.isEmpty()) {
352                             namedGroups = defaultConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
353                             logger.info("All configured namedGroups are not supported: {}. Use default: {}.",
354                                     Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS)),
355                                     Arrays.toString(DEFAULT_NAMED_GROUPS));
356                         } else {
357                             String[] groupArray = supportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
358                             if (unsupportedNamedGroups.isEmpty()) {
359                                 logger.info("Using configured namedGroups -D 'jdk.tls.namedGroup': {} ",
360                                         Arrays.toString(groupArray));
361                             } else {
362                                 logger.info("Using supported configured namedGroups: {}. Unsupported namedGroups: {}. ",
363                                         Arrays.toString(groupArray),
364                                         Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS)));
365                             }
366                             namedGroups = supportedConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
367                         }
368                     } else {
369                         namedGroups = defaultConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
370                     }
371                 } finally {
372                     SSLContext.free(sslCtx);
373                 }
374             } catch (Exception e) {
375                 logger.warn("Failed to get the list of available OpenSSL cipher suites.", e);
376             }
377             NAMED_GROUPS = namedGroups;
378             AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.unmodifiableSet(availableOpenSslCipherSuites);
379             final Set<String> availableJavaCipherSuites = new LinkedHashSet<>(
380                     AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2);
381             for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) {
382                 // Included converted but also openssl cipher name
383                 if (!isTLSv13Cipher(cipher)) {
384                     final String tlsConversion = CipherSuiteConverter.toJava(cipher, "TLS");
385                     if (tlsConversion != null) {
386                         availableJavaCipherSuites.add(tlsConversion);
387                     }
388                     final String sslConversion = CipherSuiteConverter.toJava(cipher, "SSL");
389                     if (sslConversion != null) {
390                         availableJavaCipherSuites.add(sslConversion);
391                     }
392                 } else {
393                     // TLSv1.3 ciphers have the correct format.
394                     availableJavaCipherSuites.add(cipher);
395                 }
396             }
397 
398             addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES);
399             addIfSupported(availableJavaCipherSuites, defaultCiphers, TLSV13_CIPHER_SUITES);
400             // Also handle the extra supported ciphers as these will contain some more stuff on BoringSSL.
401             addIfSupported(availableJavaCipherSuites, defaultCiphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS);
402 
403             useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites);
404             DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers);
405 
406             AVAILABLE_JAVA_CIPHER_SUITES = Collections.unmodifiableSet(availableJavaCipherSuites);
407 
408             final Set<String> availableCipherSuites = new LinkedHashSet<>(
409                     AVAILABLE_OPENSSL_CIPHER_SUITES.size() + AVAILABLE_JAVA_CIPHER_SUITES.size());
410             availableCipherSuites.addAll(AVAILABLE_OPENSSL_CIPHER_SUITES);
411             availableCipherSuites.addAll(AVAILABLE_JAVA_CIPHER_SUITES);
412 
413             AVAILABLE_CIPHER_SUITES = availableCipherSuites;
414             SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory;
415             USE_KEYMANAGER_FACTORY = useKeyManagerFactory;
416 
417             // Seems like there is no way to explicitly disable SSLv2Hello in openssl so it is always enabled
418             int supportedProtocolsPackedTemp = 0;
419             supportedProtocolsPackedTemp |= SSL_V2_HELLO;
420             if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2, SSL.SSL_OP_NO_SSLv2)) {
421                 supportedProtocolsPackedTemp |= SSL_V2;
422             }
423             if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_OP_NO_SSLv3)) {
424                 supportedProtocolsPackedTemp |= SSL_V3;
425             }
426             if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_OP_NO_TLSv1)) {
427                 supportedProtocolsPackedTemp |= TLS_V1;
428             }
429             if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1, SSL.SSL_OP_NO_TLSv1_1)) {
430                 supportedProtocolsPackedTemp |= TLS_V1_1;
431             }
432             if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_OP_NO_TLSv1_2)) {
433                 supportedProtocolsPackedTemp |= TLS_V1_2;
434             }
435 
436             // This is only supported by java8u272 and later.
437             if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)) {
438                 supportedProtocolsPackedTemp |= TLS_V1_3;
439                 TLSV13_SUPPORTED = true;
440             } else {
441                 TLSV13_SUPPORTED = false;
442             }
443 
444             supportedProtocolsPacked = supportedProtocolsPackedTemp;
445             SUPPORTS_OCSP = doesSupportOcsp();
446 
447             if (logger.isDebugEnabled()) {
448                 logger.debug("Supported protocols (OpenSSL): {} ", unpackSupportedProtocols());
449                 logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS);
450             }
451 
452             // Check if we can create a javax.security.cert.X509Certificate from our cert. This might fail on
453             // JDK17 and above. In this case we will later throw an UnsupportedOperationException if someone
454             // tries to access these via SSLSession. See https://github.com/netty/netty/issues/13560.
455             boolean javaxCertificateCreationSupported;
456             try {
457                 javax.security.cert.X509Certificate.getInstance(PROBING_CERT.getBytes(CharsetUtil.US_ASCII));
458                 javaxCertificateCreationSupported = true;
459             } catch (javax.security.cert.CertificateException ex) {
460                 javaxCertificateCreationSupported = false;
461             }
462             JAVAX_CERTIFICATE_CREATION_SUPPORTED = javaxCertificateCreationSupported;
463         } else {
464             DEFAULT_CIPHERS = Collections.emptyList();
465             AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
466             AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
467             AVAILABLE_CIPHER_SUITES = Collections.emptySet();
468             SUPPORTS_KEYMANAGER_FACTORY = false;
469             USE_KEYMANAGER_FACTORY = false;
470             SUPPORTS_OCSP = false;
471             TLSV13_SUPPORTED = false;
472             supportedProtocolsPacked = 0;
473             IS_BORINGSSL = false;
474             IS_AWSLC = false;
475             EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS;
476             EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = StringUtil.EMPTY_STRING;
477             NAMED_GROUPS = DEFAULT_NAMED_GROUPS;
478             JAVAX_CERTIFICATE_CREATION_SUPPORTED = false;
479         }
480     }
481 
482     static String checkTls13Ciphers(InternalLogger logger, String ciphers) {
483         if (IS_BORINGSSL && !ciphers.isEmpty()) {
484             assert EXTRA_SUPPORTED_TLS_1_3_CIPHERS.length > 0;
485             Set<String> boringsslTlsv13Ciphers = new HashSet<>(EXTRA_SUPPORTED_TLS_1_3_CIPHERS.length);
486             Collections.addAll(boringsslTlsv13Ciphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS);
487             boolean ciphersNotMatch = false;
488             for (String cipher: ciphers.split(":")) {
489                 if (boringsslTlsv13Ciphers.isEmpty()) {
490                     ciphersNotMatch = true;
491                     break;
492                 }
493                 if (!boringsslTlsv13Ciphers.remove(cipher) &&
494                         !boringsslTlsv13Ciphers.remove(CipherSuiteConverter.toJava(cipher, "TLS"))) {
495                     ciphersNotMatch = true;
496                     break;
497                 }
498             }
499 
500             // Also check if there are ciphers left.
501             ciphersNotMatch |= !boringsslTlsv13Ciphers.isEmpty();
502 
503             if (ciphersNotMatch) {
504                 if (logger.isInfoEnabled()) {
505                     StringBuilder javaCiphers = new StringBuilder(128);
506                     for (String cipher : ciphers.split(":")) {
507                         javaCiphers.append(CipherSuiteConverter.toJava(cipher, "TLS")).append(":");
508                     }
509                     javaCiphers.setLength(javaCiphers.length() - 1);
510                     logger.info(
511                             "BoringSSL doesn't allow to enable or disable TLSv1.3 ciphers explicitly." +
512                                     " Provided TLSv1.3 ciphers: '{}', default TLSv1.3 ciphers that will be used: '{}'.",
513                             javaCiphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING);
514                 }
515                 return EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING;
516             }
517         }
518         return ciphers;
519     }
520 
521     static boolean isSessionCacheSupported() {
522         return version() >= 0x10100000L;
523     }
524 
525     /**
526      * Returns a self-signed {@link X509Certificate} for {@code netty.io}.
527      */
528     static X509Certificate selfSignedCertificate() throws CertificateException {
529         return (X509Certificate) SslContext.X509_CERT_FACTORY.generateCertificate(
530                 new ByteArrayInputStream(PROBING_CERT.getBytes(CharsetUtil.US_ASCII))
531         );
532     }
533 
534     private static boolean doesSupportOcsp() {
535         boolean supportsOcsp = false;
536         if (version() >= 0x10002000L) {
537             long sslCtx = -1;
538             try {
539                 sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_MODE_SERVER);
540                 SSLContext.enableOcsp(sslCtx, false);
541                 supportsOcsp = true;
542             } catch (Exception ignore) {
543                 // ignore
544             } finally {
545                 if (sslCtx != -1) {
546                     SSLContext.free(sslCtx);
547                 }
548             }
549         }
550         return supportsOcsp;
551     }
552     private static boolean doesSupportProtocol(int protocol, int opt) {
553         if (opt == 0) {
554             // If the opt is 0 the protocol is not supported. This is for example the case with BoringSSL and SSLv2.
555             return false;
556         }
557         long sslCtx = -1;
558         try {
559             sslCtx = SSLContext.make(protocol, SSL.SSL_MODE_COMBINED);
560             return true;
561         } catch (Exception ignore) {
562             return false;
563         } finally {
564             if (sslCtx != -1) {
565                 SSLContext.free(sslCtx);
566             }
567         }
568     }
569 
570     /**
571      * Returns {@code true} if and only if
572      * <a href="https://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
573      * are available.
574      */
575     public static boolean isAvailable() {
576         return UNAVAILABILITY_CAUSE == null;
577     }
578 
579     /**
580      * Returns {@code true} if the used version of openssl supports
581      * <a href="https://tools.ietf.org/html/rfc7301">ALPN</a>.
582      *
583      * @deprecated use {@link SslProvider#isAlpnSupported(SslProvider)} with {@link SslProvider#OPENSSL}.
584      */
585     @Deprecated
586     public static boolean isAlpnSupported() {
587         return version() >= 0x10002000L;
588     }
589 
590     /**
591      * Returns {@code true} if the used version of OpenSSL supports OCSP stapling.
592      */
593     public static boolean isOcspSupported() {
594       return SUPPORTS_OCSP;
595     }
596 
597     /**
598      * Returns {@code true} if the used version of OpenSSL supports renegotiation.
599      * <p>
600      * Some implementations, such as BoringSSL and AWS-LC, intentionally do not support renegotiation.
601      *
602      * @return {@code true} if renegotiation is supported, otherwise {@code false}.
603      */
604     public static boolean isRenegotiationSupported() {
605         return !IS_BORINGSSL && !IS_AWSLC;
606     }
607 
608     /**
609      * Returns the version of the used available OpenSSL library or {@code -1} if {@link #isAvailable()}
610      * returns {@code false}.
611      */
612     public static int version() {
613         return isAvailable() ? SSL.version() : -1;
614     }
615 
616     /**
617      * Returns the version string of the used available OpenSSL library or {@code null} if {@link #isAvailable()}
618      * returns {@code false}.
619      */
620     public static String versionString() {
621         return isAvailable() ? SSL.versionString() : null;
622     }
623 
624     /**
625      * Ensure that <a href="https://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and
626      * its OpenSSL support are available.
627      *
628      * @throws UnsatisfiedLinkError if unavailable
629      */
630     public static void ensureAvailability() {
631         if (UNAVAILABILITY_CAUSE != null) {
632             throw (Error) new UnsatisfiedLinkError(
633                     "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
634         }
635     }
636 
637     /**
638      * Returns the cause of unavailability of
639      * <a href="https://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support.
640      *
641      * @return the cause if unavailable. {@code null} if available.
642      */
643     public static Throwable unavailabilityCause() {
644         return UNAVAILABILITY_CAUSE;
645     }
646 
647     /**
648      * @deprecated use {@link #availableOpenSslCipherSuites()}
649      */
650     @Deprecated
651     public static Set<String> availableCipherSuites() {
652         return availableOpenSslCipherSuites();
653     }
654 
655     /**
656      * Returns all the available OpenSSL cipher suites.
657      * Please note that the returned array may include the cipher suites that are insecure or non-functional.
658      */
659     public static Set<String> availableOpenSslCipherSuites() {
660         return AVAILABLE_OPENSSL_CIPHER_SUITES;
661     }
662 
663     /**
664      * Returns all the available cipher suites (Java-style).
665      * Please note that the returned array may include the cipher suites that are insecure or non-functional.
666      */
667     public static Set<String> availableJavaCipherSuites() {
668         return AVAILABLE_JAVA_CIPHER_SUITES;
669     }
670 
671     /**
672      * Returns {@code true} if and only if the specified cipher suite is available in OpenSSL.
673      * Both Java-style cipher suite and OpenSSL-style cipher suite are accepted.
674      */
675     public static boolean isCipherSuiteAvailable(String cipherSuite) {
676         String converted = CipherSuiteConverter.toOpenSsl(cipherSuite, IS_BORINGSSL);
677         if (converted != null) {
678             cipherSuite = converted;
679         }
680         return AVAILABLE_OPENSSL_CIPHER_SUITES.contains(cipherSuite);
681     }
682 
683     /**
684      * Returns {@code true} if {@link javax.net.ssl.KeyManagerFactory} is supported when using OpenSSL.
685      */
686     public static boolean supportsKeyManagerFactory() {
687         return SUPPORTS_KEYMANAGER_FACTORY;
688     }
689 
690     /**
691      * Always returns {@code true} if {@link #isAvailable()} returns {@code true}.
692      *
693      * @deprecated Will be removed because hostname validation is always done by a
694      * {@link javax.net.ssl.TrustManager} implementation.
695      */
696     @Deprecated
697     public static boolean supportsHostnameValidation() {
698         return isAvailable();
699     }
700 
701     static boolean useKeyManagerFactory() {
702         return USE_KEYMANAGER_FACTORY;
703     }
704 
705     static long memoryAddress(ByteBuf buf) {
706         assert buf.isDirect();
707         if (buf.hasMemoryAddress()) {
708             return buf.memoryAddress();
709         }
710         // Use internalNioBuffer to reduce object creation.
711         // It is important to add the position as the returned ByteBuffer might be shared by multiple ByteBuf
712         // instances and so has an address that starts before the start of the ByteBuf itself.
713         ByteBuffer byteBuffer = buf.internalNioBuffer(0, buf.readableBytes());
714         return Buffer.address(byteBuffer) + byteBuffer.position();
715     }
716 
717     private OpenSsl() { }
718 
719     private static void loadTcNative() throws Exception {
720         String os = PlatformDependent.normalizedOs();
721         String arch = PlatformDependent.normalizedArch();
722 
723         Set<String> libNames = new LinkedHashSet<String>(5);
724         String staticLibName = "netty_tcnative";
725 
726         // First, try loading the platform-specific library. Platform-specific
727         // libraries will be available if using a tcnative uber jar.
728         if ("linux".equals(os)) {
729             Set<String> classifiers = PlatformDependent.normalizedLinuxClassifiers();
730             for (String classifier : classifiers) {
731                 libNames.add(staticLibName + "_" + os + '_' + arch + "_" + classifier);
732             }
733             // generic arch-dependent library
734             libNames.add(staticLibName + "_" + os + '_' + arch);
735 
736             // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0).
737             // note: should already be included from the classifiers but if not, we use this as an
738             //       additional fallback option here
739             libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora");
740         } else {
741             libNames.add(staticLibName + "_" + os + '_' + arch);
742         }
743         libNames.add(staticLibName + "_" + arch);
744         libNames.add(staticLibName);
745 
746         NativeLibraryLoader.loadFirstAvailable(PlatformDependent.getClassLoader(SSLContext.class),
747             libNames.toArray(EmptyArrays.EMPTY_STRINGS));
748     }
749 
750     private static boolean initializeTcNative(String engine) throws Exception {
751         return Library.initialize("provided", engine);
752     }
753 
754     static void releaseIfNeeded(ReferenceCounted counted) {
755         if (counted.refCnt() > 0) {
756             ReferenceCountUtil.safeRelease(counted);
757         }
758     }
759 
760     static boolean isTlsv13Supported() {
761         return TLSV13_SUPPORTED;
762     }
763 
764     static boolean isOptionSupported(SslContextOption<?> option) {
765         if (isAvailable()) {
766             if (option == OpenSslContextOption.USE_TASKS ||
767                     option == OpenSslContextOption.TMP_DH_KEYLENGTH) {
768                 return true;
769             }
770             // Check for options that are only supported by BoringSSL atm.
771             if (isBoringSSL() || isAWSLC()) {
772                 return option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD ||
773                         option == OpenSslContextOption.PRIVATE_KEY_METHOD ||
774                         option == OpenSslContextOption.CERTIFICATE_COMPRESSION_ALGORITHMS ||
775                         option == OpenSslContextOption.TLS_FALSE_START ||
776                         option == OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES;
777             }
778         }
779         return false;
780     }
781 
782     private static Set<String> defaultProtocols(String property) {
783         String protocolsString = SystemPropertyUtil.get(property, null);
784         Set<String> protocols = new HashSet<>();
785         if (protocolsString != null) {
786             for (String proto : protocolsString.split(",")) {
787                 String p = proto.trim();
788                 protocols.add(p);
789             }
790         } else {
791             protocols.add(SslProtocols.TLS_v1_2);
792             protocols.add(SslProtocols.TLS_v1_3);
793         }
794         return protocols;
795     }
796 
797     static String[] defaultProtocols(boolean isClient) {
798         final Collection<String> defaultProtocols = isClient ? CLIENT_DEFAULT_PROTOCOLS : SERVER_DEFAULT_PROTOCOLS;
799         assert defaultProtocols != null;
800         List<String> protocols = new ArrayList<>(defaultProtocols.size());
801         for (String proto : defaultProtocols) {
802             if (isProtocolSupported(proto)) {
803                 protocols.add(proto);
804             }
805         }
806         return protocols.toArray(EmptyArrays.EMPTY_STRINGS);
807     }
808 
809     static boolean isProtocolSupported(String protocol) {
810         int bit = getProtocolBit(protocol);
811         return bit != -1 && (supportedProtocolsPacked & bit) != 0;
812     }
813 
814     private static int getProtocolBit(String protocol) {
815         if (protocol == null) {
816             return -1;
817         }
818 
819         switch (protocol) {
820             case SslProtocols.SSL_v2_HELLO:
821                 return SSL_V2_HELLO;
822             case SslProtocols.SSL_v2:
823                 return SSL_V2;
824             case SslProtocols.SSL_v3:
825                 return SSL_V3;
826             case SslProtocols.TLS_v1:
827                 return TLS_V1;
828             case SslProtocols.TLS_v1_1:
829                 return TLS_V1_1;
830             case SslProtocols.TLS_v1_2:
831                 return TLS_V1_2;
832             case SslProtocols.TLS_v1_3:
833                 return TLS_V1_3;
834             default:
835                 return -1;
836         }
837     }
838 
839     static List<String> unpackSupportedProtocols() {
840         List<String> protocols = new ArrayList<>(7);
841         if ((supportedProtocolsPacked & SSL_V2_HELLO) != 0) {
842             protocols.add(SslProtocols.SSL_v2_HELLO);
843         }
844         if ((supportedProtocolsPacked & SSL_V2) != 0) {
845             protocols.add(SslProtocols.SSL_v2);
846         }
847         if ((supportedProtocolsPacked & SSL_V3) != 0) {
848             protocols.add(SslProtocols.SSL_v3);
849         }
850         if ((supportedProtocolsPacked & TLS_V1) != 0) {
851             protocols.add(SslProtocols.TLS_v1);
852         }
853         if ((supportedProtocolsPacked & TLS_V1_1) != 0) {
854             protocols.add(SslProtocols.TLS_v1_1);
855         }
856         if ((supportedProtocolsPacked & TLS_V1_2) != 0) {
857             protocols.add(SslProtocols.TLS_v1_2);
858         }
859         if ((supportedProtocolsPacked & TLS_V1_3) != 0) {
860             protocols.add(SslProtocols.TLS_v1_3);
861         }
862         return protocols;
863     }
864 
865     static boolean isBoringSSL() {
866         return IS_BORINGSSL;
867     }
868 
869     static boolean isAWSLC() {
870         return IS_AWSLC;
871     }
872 }