Skip to content

Commit 4661630

Browse files
committed
Default using Windows Schannel for SSL/TLS on Windows
Follow https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetcertificatechain for related flags. Closes #1978
1 parent 145fc8b commit 4661630

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* HTTPLIB_REQUIRE_ZSTD (default off)
1212
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
1313
* HTTPLIB_USE_NON_BLOCKING_GETADDRINFO (default on)
14+
* HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE (default on)
1415
* HTTPLIB_COMPILE (default off)
1516
* HTTPLIB_INSTALL (default on)
1617
* HTTPLIB_TEST (default off)
@@ -109,6 +110,7 @@ option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system cer
109110
option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON)
110111
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
111112
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
113+
option(HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE "Enable automatic root certificates update on Windows." ON)
112114
# Defaults to static library
113115
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
114116
if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
@@ -274,6 +276,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
274276
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
275277
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
276278
$<$<BOOL:${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}>:CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO>
279+
$<$<AND:$<PLATFORM_ID:Windows>,$<NOT:$<BOOL:${HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE}>>>:CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE>
277280
)
278281

279282
# CMake configuration files installation directory

httplib.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ using ssize_t = long;
197197
#endif // NOMINMAX
198198

199199
#include <io.h>
200+
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && ! defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
201+
#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
202+
#endif
200203
#include <winsock2.h>
201204
#include <ws2tcpip.h>
202205

@@ -6040,6 +6043,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
60406043
}
60416044

60426045
#ifdef _WIN32
6046+
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
60436047
// NOTE: This code came up with the following stackoverflow post:
60446048
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
60456049
inline bool load_system_certs_on_windows(X509_STORE *store) {
@@ -6066,6 +6070,7 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
60666070

60676071
return result;
60686072
}
6073+
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
60696074
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
60706075
defined(TARGET_OS_OSX)
60716076
template <typename T>
@@ -10483,8 +10488,10 @@ inline bool SSLClient::load_certs() {
1048310488
} else {
1048410489
auto loaded = false;
1048510490
#ifdef _WIN32
10491+
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1048610492
loaded =
1048710493
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
10494+
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1048810495
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
1048910496
defined(TARGET_OS_OSX)
1049010497
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
@@ -10529,13 +10536,15 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1052910536
}
1053010537

1053110538
if (verification_status == SSLVerifierResponse::NoDecisionMade) {
10539+
#if ! defined(_WIN32) || defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1053210540
verify_result_ = SSL_get_verify_result(ssl2);
1053310541

1053410542
if (verify_result_ != X509_V_OK) {
1053510543
last_openssl_error_ = static_cast<unsigned long>(verify_result_);
1053610544
error = Error::SSLServerVerification;
1053710545
return false;
1053810546
}
10547+
#endif // not _WIN32 || CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1053910548

1054010549
auto server_cert = SSL_get1_peer_certificate(ssl2);
1054110550
auto se = detail::scope_exit([&] { X509_free(server_cert); });
@@ -10546,13 +10555,93 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1054610555
return false;
1054710556
}
1054810557

10558+
#if ! defined(_WIN32) || defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1054910559
if (server_hostname_verification_) {
1055010560
if (!verify_host(server_cert)) {
1055110561
last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
1055210562
error = Error::SSLServerHostnameVerification;
1055310563
return false;
1055410564
}
1055510565
}
10566+
#else // _WIN32 && ! CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
10567+
// Convert OpenSSL certificate to DER format
10568+
auto der_cert =
10569+
std::vector<unsigned char>(i2d_X509(server_cert, nullptr));
10570+
auto der_cert_data = der_cert.data();
10571+
if (i2d_X509(server_cert, &der_cert_data) < 0) {
10572+
error = Error::SSLServerVerification;
10573+
return false;
10574+
}
10575+
10576+
// Create a certificate context from the DER-encoded certificate
10577+
auto cert_context = CertCreateCertificateContext(
10578+
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
10579+
static_cast<DWORD>(der_cert.size()));
10580+
10581+
if (cert_context == nullptr) {
10582+
error = Error::SSLServerVerification;
10583+
return false;
10584+
}
10585+
10586+
auto chain_para = CERT_CHAIN_PARA{};
10587+
chain_para.cbSize = sizeof(chain_para);
10588+
chain_para.dwUrlRetrievalTimeout = 10 * 1000;
10589+
10590+
auto chain_context = PCCERT_CHAIN_CONTEXT{};
10591+
auto result = CertGetCertificateChain(
10592+
nullptr, cert_context, nullptr, cert_context->hCertStore,
10593+
&chain_para,
10594+
CERT_CHAIN_CACHE_END_CERT |
10595+
CERT_CHAIN_REVOCATION_CHECK_END_CERT |
10596+
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
10597+
nullptr, &chain_context);
10598+
10599+
CertFreeCertificateContext(cert_context);
10600+
10601+
if (!result || chain_context == nullptr) {
10602+
error = Error::SSLServerVerification;
10603+
return false;
10604+
}
10605+
10606+
// Verify chain policy
10607+
auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
10608+
extra_policy_para.cbSize = sizeof(extra_policy_para);
10609+
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
10610+
auto whost = detail::u8string_to_wstring(host_.c_str());
10611+
if (server_hostname_verification_) {
10612+
extra_policy_para.pwszServerName =
10613+
const_cast<wchar_t *>(whost.c_str());
10614+
}
10615+
10616+
auto policy_para = CERT_CHAIN_POLICY_PARA{};
10617+
policy_para.cbSize = sizeof(policy_para);
10618+
policy_para.dwFlags =
10619+
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
10620+
policy_para.pvExtraPolicyPara = &extra_policy_para;
10621+
10622+
auto policy_status = CERT_CHAIN_POLICY_STATUS{};
10623+
policy_status.cbSize = sizeof(policy_status);
10624+
10625+
result = CertVerifyCertificateChainPolicy(
10626+
CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
10627+
&policy_status);
10628+
10629+
CertFreeCertificateChain(chain_context);
10630+
10631+
if (!result) {
10632+
error = Error::SSLServerVerification;
10633+
return false;
10634+
}
10635+
10636+
if (policy_status.dwError != 0) {
10637+
if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
10638+
error = Error::SSLServerHostnameVerification;
10639+
} else {
10640+
error = Error::SSLServerVerification;
10641+
}
10642+
return false;
10643+
}
10644+
#endif // not _WIN32 || CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1055610645
}
1055710646
}
1055810647

0 commit comments

Comments
 (0)