@@ -221,6 +221,10 @@ using ssize_t = __int64;
221221#endif // NOMINMAX
222222
223223#include < io.h>
224+ #if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && \
225+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
226+ #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
227+ #endif
224228#include < winsock2.h>
225229#include < ws2tcpip.h>
226230
@@ -1349,6 +1353,17 @@ class Result {
13491353 : res_(std::move(res)), err_(err),
13501354 request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
13511355 ssl_openssl_error_(ssl_openssl_error) {}
1356+
1357+ #if defined(_WIN32) && \
1358+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1359+ Result (std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1360+ int ssl_error, unsigned long ssl_openssl_error,
1361+ unsigned long wincrypt_error, unsigned long wincrypt_chain_error)
1362+ : res_(std::move(res)), err_(err),
1363+ request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1364+ ssl_openssl_error_(ssl_openssl_error), wincrypt_error_(wincrypt_error),
1365+ wincrypt_chain_error_(wincrypt_chain_error) {}
1366+ #endif
13521367#endif
13531368 // Response
13541369 operator bool () const { return res_ != nullptr ; }
@@ -1369,6 +1384,14 @@ class Result {
13691384 int ssl_error () const { return ssl_error_; }
13701385 // OpenSSL Error
13711386 unsigned long ssl_openssl_error () const { return ssl_openssl_error_; }
1387+
1388+ #if defined(_WIN32) && \
1389+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1390+ // Windows Certificate Error (from GetLastError or policy_status.dwError)
1391+ unsigned long wincrypt_error () const { return wincrypt_error_; }
1392+ // Windows Certificate Chain Trust Status (from TrustStatus.dwErrorStatus)
1393+ unsigned long wincrypt_chain_error () const { return wincrypt_chain_error_; }
1394+ #endif
13721395#endif
13731396
13741397 // Request Headers
@@ -1387,6 +1410,12 @@ class Result {
13871410#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13881411 int ssl_error_ = 0 ;
13891412 unsigned long ssl_openssl_error_ = 0 ;
1413+
1414+ #if defined(_WIN32) && \
1415+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1416+ unsigned long wincrypt_error_ = 0 ;
1417+ unsigned long wincrypt_chain_error_ = 0 ;
1418+ #endif
13901419#endif
13911420};
13921421
@@ -1706,6 +1735,12 @@ class ClientImpl {
17061735#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17071736 int last_ssl_error_ = 0 ;
17081737 unsigned long last_openssl_error_ = 0 ;
1738+
1739+ #if defined(_WIN32) && \
1740+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1741+ unsigned long last_wincrypt_error_ = 0 ;
1742+ unsigned long last_wincrypt_chain_error_ = 0 ;
1743+ #endif
17091744#endif
17101745
17111746private:
@@ -6310,6 +6345,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
63106345}
63116346
63126347#ifdef _WIN32
6348+ #ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
63136349// NOTE: This code came up with the following stackoverflow post:
63146350// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
63156351inline bool load_system_certs_on_windows (X509_STORE *store) {
@@ -6336,6 +6372,7 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
63366372
63376373 return result;
63386374}
6375+ #endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
63396376#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
63406377template <typename T>
63416378using CFObjectPtr =
@@ -8954,8 +8991,19 @@ inline Result ClientImpl::send_(Request &&req) {
89548991 auto error = Error::Success;
89558992 auto ret = send (req, *res, error);
89568993#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8994+ #if defined(_WIN32) && \
8995+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
8996+ return Result{ret ? std::move (res) : nullptr ,
8997+ error,
8998+ std::move (req.headers ),
8999+ last_ssl_error_,
9000+ last_openssl_error_,
9001+ last_wincrypt_error_,
9002+ last_wincrypt_chain_error_};
9003+ #else
89579004 return Result{ret ? std::move (res) : nullptr , error, std::move (req.headers ),
89589005 last_ssl_error_, last_openssl_error_};
9006+ #endif
89599007#else
89609008 return Result{ret ? std::move (res) : nullptr , error, std::move (req.headers )};
89619009#endif
@@ -9541,8 +9589,19 @@ inline Result ClientImpl::send_with_content_provider_and_receiver(
95419589 std::move (content_receiver), error);
95429590
95439591#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9592+ #if defined(_WIN32) && \
9593+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
9594+ return Result{std::move (res),
9595+ error,
9596+ std::move (req.headers ),
9597+ last_ssl_error_,
9598+ last_openssl_error_,
9599+ last_wincrypt_error_,
9600+ last_wincrypt_chain_error_};
9601+ #else
95449602 return Result{std::move (res), error, std::move (req.headers ), last_ssl_error_,
95459603 last_openssl_error_};
9604+ #endif
95469605#else
95479606 return Result{std::move (res), error, std::move (req.headers )};
95489607#endif
@@ -11348,8 +11407,10 @@ inline bool SSLClient::load_certs() {
1134811407 } else {
1134911408 auto loaded = false ;
1135011409#ifdef _WIN32
11410+ #ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1135111411 loaded =
1135211412 detail::load_system_certs_on_windows (SSL_CTX_get_cert_store (ctx_));
11413+ #endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1135311414#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
1135411415 loaded = detail::load_system_certs_on_macos (SSL_CTX_get_cert_store (ctx_));
1135511416#endif // _WIN32
@@ -11396,6 +11457,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1139611457 }
1139711458
1139811459 if (verification_status == SSLVerifierResponse::NoDecisionMade) {
11460+ #if !defined(_WIN32) || \
11461+ defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1139911462 verify_result_ = SSL_get_verify_result (ssl2);
1140011463
1140111464 if (verify_result_ != X509_V_OK) {
@@ -11404,6 +11467,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1140411467 output_error_log (error, nullptr );
1140511468 return false ;
1140611469 }
11470+ #endif // !_WIN32 ||
11471+ // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1140711472
1140811473 auto server_cert = SSL_get1_peer_certificate (ssl2);
1140911474 auto se = detail::scope_exit ([&] { X509_free (server_cert); });
@@ -11415,6 +11480,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1141511480 return false ;
1141611481 }
1141711482
11483+ #if !defined(_WIN32) || \
11484+ defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1141811485 if (server_hostname_verification_) {
1141911486 if (!verify_host (server_cert)) {
1142011487 last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
@@ -11423,6 +11490,101 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1142311490 return false ;
1142411491 }
1142511492 }
11493+ #else // _WIN32 &&
11494+ // !CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
11495+ // Windows Schannel verification path - clear OpenSSL errors since
11496+ // we're not using OpenSSL for certificate verification
11497+ last_openssl_error_ = 0 ;
11498+
11499+ // Convert OpenSSL certificate to DER format
11500+ auto der_cert =
11501+ std::vector<unsigned char >(i2d_X509 (server_cert, nullptr ));
11502+ auto der_cert_data = der_cert.data ();
11503+ if (i2d_X509 (server_cert, &der_cert_data) < 0 ) {
11504+ error = Error::SSLServerVerification;
11505+ return false ;
11506+ }
11507+
11508+ // Create a certificate context from the DER-encoded certificate
11509+ auto cert_context = CertCreateCertificateContext (
11510+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data (),
11511+ static_cast <DWORD>(der_cert.size ()));
11512+
11513+ if (cert_context == nullptr ) {
11514+ last_wincrypt_error_ = GetLastError ();
11515+ error = Error::SSLServerVerification;
11516+ return false ;
11517+ }
11518+
11519+ auto chain_para = CERT_CHAIN_PARA{};
11520+ chain_para.cbSize = sizeof (chain_para);
11521+ chain_para.dwUrlRetrievalTimeout = 10 * 1000 ;
11522+
11523+ auto chain_context = PCCERT_CHAIN_CONTEXT{};
11524+ auto result = CertGetCertificateChain (
11525+ nullptr , cert_context, nullptr , cert_context->hCertStore ,
11526+ &chain_para,
11527+ CERT_CHAIN_CACHE_END_CERT |
11528+ CERT_CHAIN_REVOCATION_CHECK_END_CERT |
11529+ CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
11530+ nullptr , &chain_context);
11531+
11532+ CertFreeCertificateContext (cert_context);
11533+
11534+ if (!result || chain_context == nullptr ) {
11535+ if (!result) { last_wincrypt_error_ = GetLastError (); }
11536+ error = Error::SSLServerVerification;
11537+ return false ;
11538+ }
11539+
11540+ // Capture detailed chain trust status before using the chain
11541+ last_wincrypt_chain_error_ =
11542+ chain_context->TrustStatus .dwErrorStatus ;
11543+
11544+ // Verify chain policy
11545+ auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
11546+ extra_policy_para.cbSize = sizeof (extra_policy_para);
11547+ extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
11548+ auto whost = detail::u8string_to_wstring (host_.c_str ());
11549+ if (server_hostname_verification_) {
11550+ extra_policy_para.pwszServerName =
11551+ const_cast <wchar_t *>(whost.c_str ());
11552+ }
11553+
11554+ auto policy_para = CERT_CHAIN_POLICY_PARA{};
11555+ policy_para.cbSize = sizeof (policy_para);
11556+ policy_para.dwFlags =
11557+ CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
11558+ policy_para.pvExtraPolicyPara = &extra_policy_para;
11559+
11560+ auto policy_status = CERT_CHAIN_POLICY_STATUS{};
11561+ policy_status.cbSize = sizeof (policy_status);
11562+
11563+ result = CertVerifyCertificateChainPolicy (
11564+ CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
11565+ &policy_status);
11566+
11567+ CertFreeCertificateChain (chain_context);
11568+
11569+ if (!result) {
11570+ last_wincrypt_error_ = GetLastError ();
11571+ error = Error::SSLServerVerification;
11572+ return false ;
11573+ }
11574+
11575+ if (policy_status.dwError != 0 ) {
11576+ // Store the specific Windows certificate error code
11577+ last_wincrypt_error_ = policy_status.dwError ;
11578+
11579+ if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
11580+ error = Error::SSLServerHostnameVerification;
11581+ } else {
11582+ error = Error::SSLServerVerification;
11583+ }
11584+ return false ;
11585+ }
11586+ #endif // !_WIN32 ||
11587+ // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1142611588 }
1142711589 }
1142811590
0 commit comments