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