From eab5ea01d78feb08441049c08918e6f060112331 Mon Sep 17 00:00:00 2001 From: Jiwoo Park Date: Fri, 9 Jun 2023 16:34:51 +0900 Subject: [PATCH] Load in-memory CA certificates (#1579) * Load in-memory CA certs * Add test cases for in-memory cert loading * Don't use the IIFE style --- httplib.h | 41 +++++++++++++++++++++++++++++++++++++++++ test/test.cc | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/httplib.h b/httplib.h index 0398253..dd89dd2 100644 --- a/httplib.h +++ b/httplib.h @@ -1124,6 +1124,7 @@ public: void set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path = std::string()); void set_ca_cert_store(X509_STORE *ca_cert_store); + X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size); #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT @@ -1504,6 +1505,7 @@ public: const std::string &ca_cert_dir_path = std::string()); void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); long get_openssl_verify_result() const; @@ -1563,6 +1565,7 @@ public: bool is_valid() const override; void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); long get_openssl_verify_result() const; @@ -7590,6 +7593,35 @@ inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { ca_cert_store_ = ca_cert_store; } } + +inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, + std::size_t size) { + auto mem = BIO_new_mem_buf(ca_cert, size); + if (!mem) return nullptr; + + auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr); + if (!inf) { + BIO_free_all(mem); + return nullptr; + } + + auto cts = X509_STORE_new(); + if (cts) { + for (int first = 0, last = sk_X509_INFO_num(inf); first < last; ++first) { + auto itmp = sk_X509_INFO_value(inf, first); + if (!itmp) continue; + + if (itmp->x509) X509_STORE_add_cert(cts, itmp->x509); + + if (itmp->crl) X509_STORE_add_crl(cts, itmp->crl); + } + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free_all(mem); + + return cts; +} #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT @@ -7990,6 +8022,11 @@ inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { } } +inline void SSLClient::load_ca_cert_store(const char *ca_cert, + std::size_t size) { + set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size)); +} + inline long SSLClient::get_openssl_verify_result() const { return verify_result_; } @@ -8773,6 +8810,10 @@ inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { } } +inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { + set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size)); +} + inline long Client::get_openssl_verify_result() const { if (is_ssl_) { return static_cast(*cli_).get_openssl_verify_result(); diff --git a/test/test.cc b/test/test.cc index f573cde..3fe0330 100644 --- a/test/test.cc +++ b/test/test.cc @@ -4710,6 +4710,49 @@ TEST(SSLClientTest, ServerCertificateVerification4) { ASSERT_EQ(200, res->status); } +TEST(SSLClientTest, ServerCertificateVerification5_Online) { + std::string cert; + detail::read_file(CA_CERT_FILE, cert); + + SSLClient cli("google.com"); + cli.load_ca_cert_store(cert.data(), cert.size()); + const auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(301, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification6_Online) { + // clang-format off + static constexpr char cert[] = + "GlobalSign Root CA\n" + "==================\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\n" + "GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\n" + "b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\n" + "BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\n" + "VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\n" + "DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\n" + "THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\n" + "Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\n" + "c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\n" + "gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\n" + "AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\n" + "Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\n" + "j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\n" + "hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\n" + "X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" + "-----END CERTIFICATE-----\n"; + // clang-format on + + SSLClient cli("google.com"); + cli.load_ca_cert_store(cert, sizeof(cert)); + const auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(301, res->status); +} + TEST(SSLClientTest, WildcardHostNameMatch_Online) { SSLClient cli("www.youtube.com");