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