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