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