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