xref: /openbmc/bmcweb/include/ssl_key_handler.hpp (revision 099225cc9300c8e06b742a48318df75b0366561f)
1 #pragma once
2 
3 #include "bmcweb_config.h"
4 
5 #include "logging.hpp"
6 #include "ossl_random.hpp"
7 
8 #include <boost/beast/core/file_posix.hpp>
9 
10 extern "C"
11 {
12 #include <nghttp2/nghttp2.h>
13 #include <openssl/bio.h>
14 #include <openssl/dh.h>
15 #include <openssl/dsa.h>
16 #include <openssl/err.h>
17 #include <openssl/evp.h>
18 #include <openssl/pem.h>
19 #include <openssl/rand.h>
20 #include <openssl/rsa.h>
21 #include <openssl/ssl.h>
22 }
23 
24 #include <boost/asio/ssl/context.hpp>
25 #include <boost/system/error_code.hpp>
26 
27 #include <optional>
28 #include <random>
29 #include <string>
30 
31 namespace ensuressl
32 {
33 constexpr const char* trustStorePath = "/etc/ssl/certs/authority";
34 constexpr const char* x509Comment = "Generated from OpenBMC service";
35 static void initOpenssl();
36 static EVP_PKEY* createEcKey();
37 
38 // Trust chain related errors.`
39 inline bool isTrustChainError(int errnum)
40 {
41     return (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||
42            (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) ||
43            (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) ||
44            (errnum == X509_V_ERR_CERT_UNTRUSTED) ||
45            (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
46 }
47 
48 inline bool validateCertificate(X509* const cert)
49 {
50     // Create an empty X509_STORE structure for certificate validation.
51     X509_STORE* x509Store = X509_STORE_new();
52     if (x509Store == nullptr)
53     {
54         BMCWEB_LOG_ERROR("Error occurred during X509_STORE_new call");
55         return false;
56     }
57 
58     // Load Certificate file into the X509 structure.
59     X509_STORE_CTX* storeCtx = X509_STORE_CTX_new();
60     if (storeCtx == nullptr)
61     {
62         BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_new call");
63         X509_STORE_free(x509Store);
64         return false;
65     }
66 
67     int errCode = X509_STORE_CTX_init(storeCtx, x509Store, cert, nullptr);
68     if (errCode != 1)
69     {
70         BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_init call");
71         X509_STORE_CTX_free(storeCtx);
72         X509_STORE_free(x509Store);
73         return false;
74     }
75 
76     errCode = X509_verify_cert(storeCtx);
77     if (errCode == 1)
78     {
79         BMCWEB_LOG_INFO("Certificate verification is success");
80         X509_STORE_CTX_free(storeCtx);
81         X509_STORE_free(x509Store);
82         return true;
83     }
84     if (errCode == 0)
85     {
86         errCode = X509_STORE_CTX_get_error(storeCtx);
87         X509_STORE_CTX_free(storeCtx);
88         X509_STORE_free(x509Store);
89         if (isTrustChainError(errCode))
90         {
91             BMCWEB_LOG_DEBUG("Ignoring Trust Chain error. Reason: {}",
92                              X509_verify_cert_error_string(errCode));
93             return true;
94         }
95         BMCWEB_LOG_ERROR("Certificate verification failed. Reason: {}",
96                          X509_verify_cert_error_string(errCode));
97         return false;
98     }
99 
100     BMCWEB_LOG_ERROR(
101         "Error occurred during X509_verify_cert call. ErrorCode: {}", errCode);
102     X509_STORE_CTX_free(storeCtx);
103     X509_STORE_free(x509Store);
104     return false;
105 }
106 
107 inline std::string verifyOpensslKeyCert(const std::string& filepath)
108 {
109     bool privateKeyValid = false;
110 
111     BMCWEB_LOG_INFO("Checking certs in file {}", filepath);
112     boost::beast::file_posix file;
113     boost::system::error_code ec;
114     file.open(filepath.c_str(), boost::beast::file_mode::read, ec);
115     if (ec)
116     {
117         return "";
118     }
119     bool certValid = false;
120     std::string fileContents;
121     fileContents.resize(static_cast<size_t>(file.size(ec)), '\0');
122     file.read(fileContents.data(), fileContents.size(), ec);
123     if (ec)
124     {
125         BMCWEB_LOG_ERROR("Failed to read file");
126         return "";
127     }
128 
129     BIO* bufio = BIO_new_mem_buf(static_cast<void*>(fileContents.data()),
130                                  static_cast<int>(fileContents.size()));
131     EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bufio, nullptr, nullptr, nullptr);
132     BIO_free(bufio);
133     if (pkey != nullptr)
134     {
135 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
136         RSA* rsa = EVP_PKEY_get1_RSA(pkey);
137         if (rsa != nullptr)
138         {
139             BMCWEB_LOG_INFO("Found an RSA key");
140             if (RSA_check_key(rsa) == 1)
141             {
142                 privateKeyValid = true;
143             }
144             else
145             {
146                 BMCWEB_LOG_ERROR("Key not valid error number {}",
147                                  ERR_get_error());
148             }
149             RSA_free(rsa);
150         }
151         else
152         {
153             EC_KEY* ec = EVP_PKEY_get1_EC_KEY(pkey);
154             if (ec != nullptr)
155             {
156                 BMCWEB_LOG_INFO("Found an EC key");
157                 if (EC_KEY_check_key(ec) == 1)
158                 {
159                     privateKeyValid = true;
160                 }
161                 else
162                 {
163                     BMCWEB_LOG_ERROR("Key not valid error number {}",
164                                      ERR_get_error());
165                 }
166                 EC_KEY_free(ec);
167             }
168         }
169 #else
170         EVP_PKEY_CTX* pkeyCtx = EVP_PKEY_CTX_new_from_pkey(nullptr, pkey,
171                                                            nullptr);
172 
173         if (pkeyCtx == nullptr)
174         {
175             BMCWEB_LOG_ERROR("Unable to allocate pkeyCtx {}", ERR_get_error());
176         }
177         else if (EVP_PKEY_check(pkeyCtx) == 1)
178         {
179             privateKeyValid = true;
180         }
181         else
182         {
183             BMCWEB_LOG_ERROR("Key not valid error number {}", ERR_get_error());
184         }
185 #endif
186 
187         if (privateKeyValid)
188         {
189             BIO* bufio2 =
190                 BIO_new_mem_buf(static_cast<void*>(fileContents.data()),
191                                 static_cast<int>(fileContents.size()));
192             X509* x509 = PEM_read_bio_X509(bufio2, nullptr, nullptr, nullptr);
193             BIO_free(bufio2);
194             if (x509 == nullptr)
195             {
196                 BMCWEB_LOG_ERROR("error getting x509 cert {}", ERR_get_error());
197             }
198             else
199             {
200                 certValid = validateCertificate(x509);
201                 X509_free(x509);
202             }
203         }
204 
205 #if (OPENSSL_VERSION_NUMBER > 0x30000000L)
206         EVP_PKEY_CTX_free(pkeyCtx);
207 #endif
208         EVP_PKEY_free(pkey);
209     }
210     if (!certValid)
211     {
212         return "";
213     }
214     return fileContents;
215 }
216 
217 inline X509* loadCert(const std::string& filePath)
218 {
219     BIO* certFileBio = BIO_new_file(filePath.c_str(), "rb");
220     if (certFileBio == nullptr)
221     {
222         BMCWEB_LOG_ERROR("Error occurred during BIO_new_file call, FILE= {}",
223                          filePath);
224         return nullptr;
225     }
226 
227     X509* cert = X509_new();
228     if (cert == nullptr)
229     {
230         BMCWEB_LOG_ERROR("Error occurred during X509_new call, {}",
231                          ERR_get_error());
232         BIO_free(certFileBio);
233         return nullptr;
234     }
235 
236     if (PEM_read_bio_X509(certFileBio, &cert, nullptr, nullptr) == nullptr)
237     {
238         BMCWEB_LOG_ERROR(
239             "Error occurred during PEM_read_bio_X509 call, FILE= {}", filePath);
240 
241         BIO_free(certFileBio);
242         X509_free(cert);
243         return nullptr;
244     }
245     BIO_free(certFileBio);
246     return cert;
247 }
248 
249 inline int addExt(X509* cert, int nid, const char* value)
250 {
251     X509_EXTENSION* ex = nullptr;
252     X509V3_CTX ctx{};
253     X509V3_set_ctx(&ctx, cert, cert, nullptr, nullptr, 0);
254 
255     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
256     ex = X509V3_EXT_conf_nid(nullptr, &ctx, nid, const_cast<char*>(value));
257     if (ex == nullptr)
258     {
259         BMCWEB_LOG_ERROR("Error: In X509V3_EXT_conf_nidn: {}", value);
260         return -1;
261     }
262     X509_add_ext(cert, ex, -1);
263     X509_EXTENSION_free(ex);
264     return 0;
265 }
266 
267 // Writes a certificate to a path, ignoring errors
268 inline void writeCertificateToFile(const std::string& filepath,
269                                    const std::string& certificate)
270 {
271     boost::system::error_code ec;
272     boost::beast::file_posix file;
273     file.open(filepath.c_str(), boost::beast::file_mode::write, ec);
274     if (!ec)
275     {
276         file.write(certificate.data(), certificate.size(), ec);
277         // ignore result
278     }
279 }
280 
281 inline std::string generateSslCertificate(const std::string& cn)
282 {
283     BMCWEB_LOG_INFO("Generating new keys");
284     initOpenssl();
285 
286     std::string buffer;
287     BMCWEB_LOG_INFO("Generating EC key");
288     EVP_PKEY* pPrivKey = createEcKey();
289     if (pPrivKey != nullptr)
290     {
291         BMCWEB_LOG_INFO("Generating x509 Certificates");
292         // Use this code to directly generate a certificate
293         X509* x509 = X509_new();
294         if (x509 != nullptr)
295         {
296             // get a random number from the RNG for the certificate serial
297             // number If this is not random, regenerating certs throws browser
298             // errors
299             bmcweb::OpenSSLGenerator gen;
300             std::uniform_int_distribution<int> dis(
301                 1, std::numeric_limits<int>::max());
302             int serial = dis(gen);
303 
304             ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
305 
306             // not before this moment
307             X509_gmtime_adj(X509_get_notBefore(x509), 0);
308             // Cert is valid for 10 years
309             X509_gmtime_adj(X509_get_notAfter(x509),
310                             60L * 60L * 24L * 365L * 10L);
311 
312             // set the public key to the key we just generated
313             X509_set_pubkey(x509, pPrivKey);
314 
315             // get the subject name
316             X509_NAME* name = X509_get_subject_name(x509);
317 
318             using x509String = const unsigned char;
319             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
320             x509String* country = reinterpret_cast<x509String*>("US");
321             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
322             x509String* company = reinterpret_cast<x509String*>("OpenBMC");
323             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
324             x509String* cnStr = reinterpret_cast<x509String*>(cn.c_str());
325 
326             X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1,
327                                        0);
328             X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1,
329                                        0);
330             X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cnStr, -1, -1,
331                                        0);
332             // set the CSR options
333             X509_set_issuer_name(x509, name);
334 
335             X509_set_version(x509, 2);
336             addExt(x509, NID_basic_constraints, ("critical,CA:TRUE"));
337             addExt(x509, NID_subject_alt_name, ("DNS:" + cn).c_str());
338             addExt(x509, NID_subject_key_identifier, ("hash"));
339             addExt(x509, NID_authority_key_identifier, ("keyid"));
340             addExt(x509, NID_key_usage, ("digitalSignature, keyEncipherment"));
341             addExt(x509, NID_ext_key_usage, ("serverAuth"));
342             addExt(x509, NID_netscape_comment, (x509Comment));
343 
344             // Sign the certificate with our private key
345             X509_sign(x509, pPrivKey, EVP_sha256());
346 
347             BIO* bufio = BIO_new(BIO_s_mem());
348 
349             int pkeyRet = PEM_write_bio_PrivateKey(
350                 bufio, pPrivKey, nullptr, nullptr, 0, nullptr, nullptr);
351             if (pkeyRet <= 0)
352             {
353                 BMCWEB_LOG_ERROR(
354                     "Failed to write pkey with code {}.  Ignoring.", pkeyRet);
355             }
356 
357             char* data = nullptr;
358             long int dataLen = BIO_get_mem_data(bufio, &data);
359             buffer += std::string_view(data, static_cast<size_t>(dataLen));
360             BIO_free(bufio);
361 
362             bufio = BIO_new(BIO_s_mem());
363             pkeyRet = PEM_write_bio_X509(bufio, x509);
364             if (pkeyRet <= 0)
365             {
366                 BMCWEB_LOG_ERROR(
367                     "Failed to write X509 with code {}.  Ignoring.", pkeyRet);
368             }
369             dataLen = BIO_get_mem_data(bufio, &data);
370             buffer += std::string_view(data, static_cast<size_t>(dataLen));
371 
372             BIO_free(bufio);
373             BMCWEB_LOG_INFO("Cert size is {}", buffer.size());
374             X509_free(x509);
375         }
376 
377         EVP_PKEY_free(pPrivKey);
378         pPrivKey = nullptr;
379     }
380 
381     // cleanup_openssl();
382     return buffer;
383 }
384 
385 EVP_PKEY* createEcKey()
386 {
387     EVP_PKEY* pKey = nullptr;
388 
389 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
390     int eccgrp = 0;
391     eccgrp = OBJ_txt2nid("secp384r1");
392 
393     EC_KEY* myecc = EC_KEY_new_by_curve_name(eccgrp);
394     if (myecc != nullptr)
395     {
396         EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
397         EC_KEY_generate_key(myecc);
398         pKey = EVP_PKEY_new();
399         if (pKey != nullptr)
400         {
401             if (EVP_PKEY_assign(pKey, EVP_PKEY_EC, myecc) != 0)
402             {
403                 /* pKey owns myecc from now */
404                 if (EC_KEY_check_key(myecc) <= 0)
405                 {
406                     BMCWEB_LOG_ERROR("EC_check_key failed.");
407                 }
408             }
409         }
410     }
411 #else
412     // Create context for curve parameter generation.
413     std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)> ctx{
414         EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free};
415     if (!ctx)
416     {
417         return nullptr;
418     }
419 
420     // Set up curve parameters.
421     EVP_PKEY* params = nullptr;
422     if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
423         (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
424          0) ||
425         (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1) <=
426          0) ||
427         (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
428     {
429         return nullptr;
430     }
431 
432     // Set up RAII holder for params.
433     std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)> pparams{
434         params, &::EVP_PKEY_free};
435 
436     // Set new context for key generation, using curve parameters.
437     ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
438     if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
439     {
440         return nullptr;
441     }
442 
443     // Generate key.
444     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
445     {
446         return nullptr;
447     }
448 #endif
449 
450     return pKey;
451 }
452 
453 void initOpenssl()
454 {
455 #if OPENSSL_VERSION_NUMBER < 0x10100000L
456     SSL_load_error_strings();
457     OpenSSL_add_all_algorithms();
458     RAND_load_file("/dev/urandom", 1024);
459 #endif
460 }
461 
462 inline std::string ensureOpensslKeyPresentAndValid(const std::string& filepath)
463 {
464     std::string cert = verifyOpensslKeyCert(filepath);
465 
466     if (cert.empty())
467     {
468         BMCWEB_LOG_WARNING("Error in verifying signature, regenerating");
469         cert = generateSslCertificate("testhost");
470         if (cert.empty())
471         {
472             BMCWEB_LOG_ERROR("Failed to generate cert");
473         }
474         else
475         {
476             writeCertificateToFile(filepath, cert);
477         }
478     }
479     return cert;
480 }
481 
482 inline int nextProtoCallback(SSL* /*unused*/, const unsigned char** data,
483                              unsigned int* len, void* /*unused*/)
484 {
485     // First byte is the length.
486     constexpr std::string_view h2 = "\x02h2";
487     *data = std::bit_cast<const unsigned char*>(h2.data());
488     *len = static_cast<unsigned int>(h2.size());
489     return SSL_TLSEXT_ERR_OK;
490 }
491 
492 inline int alpnSelectProtoCallback(SSL* /*unused*/, const unsigned char** out,
493                                    unsigned char* outlen,
494                                    const unsigned char* in, unsigned int inlen,
495                                    void* /*unused*/)
496 {
497     // There's a mismatch in constness for nghttp2_select_next_protocol.  The
498     // examples in nghttp2 don't show this problem.  Unclear what the right fix
499     // is here.
500 
501     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
502     unsigned char** outNew = const_cast<unsigned char**>(out);
503     int rv = nghttp2_select_next_protocol(outNew, outlen, in, inlen);
504     if (rv != 1)
505     {
506         return SSL_TLSEXT_ERR_NOACK;
507     }
508 
509     return SSL_TLSEXT_ERR_OK;
510 }
511 
512 inline std::shared_ptr<boost::asio::ssl::context>
513     getSslContext(const std::string& sslPemFile)
514 {
515     std::shared_ptr<boost::asio::ssl::context> mSslContext =
516         std::make_shared<boost::asio::ssl::context>(
517             boost::asio::ssl::context::tls_server);
518     mSslContext->set_options(boost::asio::ssl::context::default_workarounds |
519                              boost::asio::ssl::context::no_sslv2 |
520                              boost::asio::ssl::context::no_sslv3 |
521                              boost::asio::ssl::context::single_dh_use |
522                              boost::asio::ssl::context::no_tlsv1 |
523                              boost::asio::ssl::context::no_tlsv1_1);
524 
525     // BIG WARNING: This needs to stay disabled, as there will always be
526     // unauthenticated endpoints
527     // mSslContext->set_verify_mode(boost::asio::ssl::verify_peer);
528 
529     SSL_CTX_set_options(mSslContext->native_handle(), SSL_OP_NO_RENEGOTIATION);
530 
531     BMCWEB_LOG_DEBUG("Using default TrustStore location: {}", trustStorePath);
532     mSslContext->add_verify_path(trustStorePath);
533 
534     boost::system::error_code ec;
535     boost::asio::const_buffer buf(sslPemFile.data(), sslPemFile.size());
536     mSslContext->use_certificate(buf, boost::asio::ssl::context::pem, ec);
537     if (ec)
538     {
539         BMCWEB_LOG_CRITICAL("Failed to open ssl certificate");
540         return nullptr;
541     }
542     mSslContext->use_private_key(buf, boost::asio::ssl::context::pem);
543     if (ec)
544     {
545         BMCWEB_LOG_CRITICAL("Failed to open ssl pkey");
546         return nullptr;
547     }
548 
549     if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
550     {
551         SSL_CTX_set_next_protos_advertised_cb(mSslContext->native_handle(),
552                                               nextProtoCallback, nullptr);
553 
554         SSL_CTX_set_alpn_select_cb(mSslContext->native_handle(),
555                                    alpnSelectProtoCallback, nullptr);
556     }
557     // Set up EC curves to auto (boost asio doesn't have a method for this)
558     // There is a pull request to add this.  Once this is included in an asio
559     // drop, use the right way
560     // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
561     if (SSL_CTX_set_ecdh_auto(mSslContext->native_handle(), 1) != 1)
562     {}
563 
564     // Mozilla intermediate cipher suites v5.7
565     // Sourced from: https://ssl-config.mozilla.org/guidelines/5.7.json
566     const char* mozillaIntermediate = "ECDHE-ECDSA-AES128-GCM-SHA256:"
567                                       "ECDHE-RSA-AES128-GCM-SHA256:"
568                                       "ECDHE-ECDSA-AES256-GCM-SHA384:"
569                                       "ECDHE-RSA-AES256-GCM-SHA384:"
570                                       "ECDHE-ECDSA-CHACHA20-POLY1305:"
571                                       "ECDHE-RSA-CHACHA20-POLY1305:"
572                                       "DHE-RSA-AES128-GCM-SHA256:"
573                                       "DHE-RSA-AES256-GCM-SHA384:"
574                                       "DHE-RSA-CHACHA20-POLY1305";
575 
576     if (SSL_CTX_set_cipher_list(mSslContext->native_handle(),
577                                 mozillaIntermediate) != 1)
578     {
579         BMCWEB_LOG_ERROR("Error setting cipher list");
580     }
581     return mSslContext;
582 }
583 
584 inline std::optional<boost::asio::ssl::context> getSSLClientContext()
585 {
586     boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_client);
587 
588     boost::system::error_code ec;
589 
590     // Support only TLS v1.2 & v1.3
591     sslCtx.set_options(boost::asio::ssl::context::default_workarounds |
592                            boost::asio::ssl::context::no_sslv2 |
593                            boost::asio::ssl::context::no_sslv3 |
594                            boost::asio::ssl::context::single_dh_use |
595                            boost::asio::ssl::context::no_tlsv1 |
596                            boost::asio::ssl::context::no_tlsv1_1,
597                        ec);
598     if (ec)
599     {
600         BMCWEB_LOG_ERROR("SSL context set_options failed");
601         return std::nullopt;
602     }
603 
604     // Add a directory containing certificate authority files to be used
605     // for performing verification.
606     sslCtx.set_default_verify_paths(ec);
607     if (ec)
608     {
609         BMCWEB_LOG_ERROR("SSL context set_default_verify failed");
610         return std::nullopt;
611     }
612 
613     // Verify the remote server's certificate
614     sslCtx.set_verify_mode(boost::asio::ssl::verify_peer, ec);
615     if (ec)
616     {
617         BMCWEB_LOG_ERROR("SSL context set_verify_mode failed");
618         return std::nullopt;
619     }
620 
621     // All cipher suites are set as per OWASP datasheet.
622     // https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html
623     constexpr const char* sslCiphers = "ECDHE-ECDSA-AES128-GCM-SHA256:"
624                                        "ECDHE-RSA-AES128-GCM-SHA256:"
625                                        "ECDHE-ECDSA-AES256-GCM-SHA384:"
626                                        "ECDHE-RSA-AES256-GCM-SHA384:"
627                                        "ECDHE-ECDSA-CHACHA20-POLY1305:"
628                                        "ECDHE-RSA-CHACHA20-POLY1305:"
629                                        "DHE-RSA-AES128-GCM-SHA256:"
630                                        "DHE-RSA-AES256-GCM-SHA384"
631                                        "TLS_AES_128_GCM_SHA256:"
632                                        "TLS_AES_256_GCM_SHA384:"
633                                        "TLS_CHACHA20_POLY1305_SHA256";
634 
635     if (SSL_CTX_set_cipher_list(sslCtx.native_handle(), sslCiphers) != 1)
636     {
637         BMCWEB_LOG_ERROR("SSL_CTX_set_cipher_list failed");
638         return std::nullopt;
639     }
640 
641     return sslCtx;
642 }
643 
644 } // namespace ensuressl
645