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