Generate cert.h
for loading in-memory cert
Require C++17 due to inline
variable
Option | Default | Description |
---|---|---|
CERT_SOURCE |
(undefined) |
Specify the location of certificates (URL or file path) |
CERT_INSTALL |
OFF |
Install cert.h and CMake targets |
CERT_SOURCE
- When this isn't defined, download the latest CA bundle maintained by curl
Note
If you want to use a local PEM file, CERT_SOURCE
must be an absolute path.
cmake --list-presets all # List all CMake presets
cmake --preset windows # Configure
cmake --build --preset windows # Build
ctest --preset windows # Test
cmake --build --preset windows -t install # Install
Require CMake 3.23+
include(FetchContent)
FetchContent_Declare(
cert-cmake
URL https://github.com/jimmy-park/cert-cmake/archive/main.tar.gz
)
FetchContent_MakeAvailable(cert-cmake)
# If you're using CPM.cmake
# CPMAddPackage(
# NAME cert-cmake
# URL https://github.com/jimmy-park/cert-cmake/archive/main.tar.gz
# )
add_executable(main main.cpp)
target_link_libraries(main PRIVATE cert::cert)
static constexpr auto blob = curl_blob {
const_cast<char*>(kCert),
sizeof(kCert),
CURL_BLOB_NOCOPY
};
auto* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob);
curl_easy_setopt(curl, CURLOPT_URL, "https://nghttp2.org/httpbin/get");
curl_easy_perform(curl);
curl_easy_cleanup(curl);
template <auto func>
struct Deleter {
template <typename T>
constexpr void operator()(T* ptr) const { func(ptr); }
};
using X509Info = STACK_OF(X509_INFO);
void FreeX509Info(X509Info* ptr) { sk_X509_INFO_pop_free(ptr, X509_INFO_free); }
using X509Ptr = std::unique_ptr<X509Info, Deleter<FreeX509Info>>;
using BioPtr = std::unique_ptr<BIO, Deleter<BIO_free_all>>;
void LoadCert(void* ssl_context)
{
// https://www.openssl.org/docs/manmaster/man3/OPENSSL_thread_stop.html
struct OpenSSLCleaner {
~OpenSSLCleaner() { OPENSSL_thread_stop(); }
};
thread_local const OpenSSLCleaner openssl_clenaer;
static const auto x509_ptr = [] {
const auto bio_ptr = BioPtr { BIO_new_mem_buf(kCert, sizeof(kCert)) };
return X509Ptr { PEM_X509_INFO_read_bio(bio_ptr.get(), nullptr, nullptr, nullptr) };
}();
static const auto certs = [] {
std::vector<X509*> x509s;
for (int first = 0, last = sk_X509_INFO_num(x509_ptr.get()); first < last; ++first) {
auto* value = sk_X509_INFO_value(x509_ptr.get(), first);
if (value && value->x509)
x509s.push_back(value->x509);
}
return x509s;
}();
if (!ssl_context)
return;
auto* x509_store = SSL_CTX_get_cert_store(static_cast<SSL_CTX*>(ssl_context));
for (const auto x509 : certs)
X509_STORE_add_cert(x509_store, x509);
}
auto ssl_callback = +[](CURL*, void* ssl_ctx, void*) {
LoadCert(ssl_ctx);
return CURLE_OK;
};
auto* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_callback);
curl_easy_setopt(curl, CURLOPT_URL, "https://nghttp2.org/httpbin/get");
curl_easy_perform(curl);
curl_easy_cleanup(curl);