@@ -197,6 +197,10 @@ using ssize_t = long;
197197#endif // NOMINMAX
198198
199199#include < io.h>
200+ #if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && \
201+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
202+ #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
203+ #endif
200204#include < winsock2.h>
201205#include < ws2tcpip.h>
202206
@@ -1275,6 +1279,17 @@ class Result {
12751279 : res_(std::move(res)), err_(err),
12761280 request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
12771281 ssl_openssl_error_(ssl_openssl_error) {}
1282+
1283+ #if defined(_WIN32) && \
1284+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1285+ Result (std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1286+ int ssl_error, unsigned long ssl_openssl_error,
1287+ unsigned long wincrypt_error, unsigned long wincrypt_chain_error)
1288+ : res_(std::move(res)), err_(err),
1289+ request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1290+ ssl_openssl_error_(ssl_openssl_error), wincrypt_error_(wincrypt_error),
1291+ wincrypt_chain_error_(wincrypt_chain_error) {}
1292+ #endif
12781293#endif
12791294 // Response
12801295 operator bool () const { return res_ != nullptr ; }
@@ -1295,6 +1310,14 @@ class Result {
12951310 int ssl_error () const { return ssl_error_; }
12961311 // OpenSSL Error
12971312 unsigned long ssl_openssl_error () const { return ssl_openssl_error_; }
1313+
1314+ #if defined(_WIN32) && \
1315+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1316+ // Windows Certificate Error (from GetLastError or policy_status.dwError)
1317+ unsigned long wincrypt_error () const { return wincrypt_error_; }
1318+ // Windows Certificate Chain Trust Status (from TrustStatus.dwErrorStatus)
1319+ unsigned long wincrypt_chain_error () const { return wincrypt_chain_error_; }
1320+ #endif
12981321#endif
12991322
13001323 // Request Headers
@@ -1313,6 +1336,12 @@ class Result {
13131336#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13141337 int ssl_error_ = 0 ;
13151338 unsigned long ssl_openssl_error_ = 0 ;
1339+
1340+ #if defined(_WIN32) && \
1341+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1342+ unsigned long wincrypt_error_ = 0 ;
1343+ unsigned long wincrypt_chain_error_ = 0 ;
1344+ #endif
13161345#endif
13171346};
13181347
@@ -1614,6 +1643,12 @@ class ClientImpl {
16141643#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16151644 int last_ssl_error_ = 0 ;
16161645 unsigned long last_openssl_error_ = 0 ;
1646+
1647+ #if defined(_WIN32) && \
1648+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1649+ unsigned long last_wincrypt_error_ = 0 ;
1650+ unsigned long last_wincrypt_chain_error_ = 0 ;
1651+ #endif
16171652#endif
16181653
16191654private:
@@ -6040,6 +6075,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
60406075}
60416076
60426077#ifdef _WIN32
6078+ #ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
60436079// NOTE: This code came up with the following stackoverflow post:
60446080// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
60456081inline bool load_system_certs_on_windows (X509_STORE *store) {
@@ -6066,6 +6102,7 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
60666102
60676103 return result;
60686104}
6105+ #endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
60696106#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
60706107 defined (TARGET_OS_OSX)
60716108template <typename T>
@@ -8344,8 +8381,15 @@ inline Result ClientImpl::send_(Request &&req) {
83448381 auto error = Error::Success;
83458382 auto ret = send (req, *res, error);
83468383#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8384+ #if defined(_WIN32) && \
8385+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
8386+ return Result{ret ? std::move (res) : nullptr , error, std::move (req.headers ),
8387+ last_ssl_error_, last_openssl_error_, last_wincrypt_error_,
8388+ last_wincrypt_chain_error_};
8389+ #else
83478390 return Result{ret ? std::move (res) : nullptr , error, std::move (req.headers ),
83488391 last_ssl_error_, last_openssl_error_};
8392+ #endif
83498393#else
83508394 return Result{ret ? std::move (res) : nullptr , error, std::move (req.headers )};
83518395#endif
@@ -8899,8 +8943,15 @@ inline Result ClientImpl::send_with_content_provider(
88998943 std::move (content_provider_without_length), content_type, error);
89008944
89018945#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8946+ #if defined(_WIN32) && \
8947+ !defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
8948+ return Result{std::move (res), error, std::move (req.headers ), last_ssl_error_,
8949+ last_openssl_error_, last_wincrypt_error_,
8950+ last_wincrypt_chain_error_};
8951+ #else
89028952 return Result{std::move (res), error, std::move (req.headers ), last_ssl_error_,
89038953 last_openssl_error_};
8954+ #endif
89048955#else
89058956 return Result{std::move (res), error, std::move (req.headers )};
89068957#endif
@@ -10483,8 +10534,10 @@ inline bool SSLClient::load_certs() {
1048310534 } else {
1048410535 auto loaded = false ;
1048510536#ifdef _WIN32
10537+ #ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1048610538 loaded =
1048710539 detail::load_system_certs_on_windows (SSL_CTX_get_cert_store (ctx_));
10540+ #endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1048810541#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
1048910542 defined (TARGET_OS_OSX)
1049010543 loaded = detail::load_system_certs_on_macos (SSL_CTX_get_cert_store (ctx_));
@@ -10529,13 +10582,17 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1052910582 }
1053010583
1053110584 if (verification_status == SSLVerifierResponse::NoDecisionMade) {
10585+ #if !defined(_WIN32) || \
10586+ defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1053210587 verify_result_ = SSL_get_verify_result (ssl2);
1053310588
1053410589 if (verify_result_ != X509_V_OK) {
1053510590 last_openssl_error_ = static_cast <unsigned long >(verify_result_);
1053610591 error = Error::SSLServerVerification;
1053710592 return false ;
1053810593 }
10594+ #endif // not _WIN32 ||
10595+ // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1053910596
1054010597 auto server_cert = SSL_get1_peer_certificate (ssl2);
1054110598 auto se = detail::scope_exit ([&] { X509_free (server_cert); });
@@ -10546,13 +10603,110 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1054610603 return false ;
1054710604 }
1054810605
10606+ #if !defined(_WIN32) || \
10607+ defined (CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1054910608 if (server_hostname_verification_) {
1055010609 if (!verify_host (server_cert)) {
1055110610 last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
1055210611 error = Error::SSLServerHostnameVerification;
1055310612 return false ;
1055410613 }
1055510614 }
10615+ #else // _WIN32 && !
10616+ // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
10617+ // Windows Schannel verification path - clear OpenSSL errors since
10618+ // we're not using OpenSSL for certificate verification
10619+ last_openssl_error_ = 0 ;
10620+
10621+ // Convert OpenSSL certificate to DER format
10622+ auto der_cert =
10623+ std::vector<unsigned char >(i2d_X509 (server_cert, nullptr ));
10624+ auto der_cert_data = der_cert.data ();
10625+ if (i2d_X509 (server_cert, &der_cert_data) < 0 ) {
10626+ error = Error::SSLServerVerification;
10627+ return false ;
10628+ }
10629+
10630+ // Create a certificate context from the DER-encoded certificate
10631+ auto cert_context = CertCreateCertificateContext (
10632+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data (),
10633+ static_cast <DWORD>(der_cert.size ()));
10634+
10635+ if (cert_context == nullptr ) {
10636+ last_wincrypt_error_ = GetLastError ();
10637+ error = Error::SSLServerVerification;
10638+ return false ;
10639+ }
10640+
10641+ auto chain_para = CERT_CHAIN_PARA{};
10642+ chain_para.cbSize = sizeof (chain_para);
10643+ chain_para.dwUrlRetrievalTimeout = 10 * 1000 ;
10644+
10645+ auto chain_context = PCCERT_CHAIN_CONTEXT{};
10646+ auto result = CertGetCertificateChain (
10647+ nullptr , cert_context, nullptr , cert_context->hCertStore ,
10648+ &chain_para,
10649+ CERT_CHAIN_CACHE_END_CERT |
10650+ CERT_CHAIN_REVOCATION_CHECK_END_CERT |
10651+ CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
10652+ nullptr , &chain_context);
10653+
10654+ CertFreeCertificateContext (cert_context);
10655+
10656+ if (!result || chain_context == nullptr ) {
10657+ if (!result) {
10658+ last_wincrypt_error_ = GetLastError ();
10659+ }
10660+ error = Error::SSLServerVerification;
10661+ return false ;
10662+ }
10663+
10664+ // Capture detailed chain trust status before using the chain
10665+ last_wincrypt_chain_error_ = chain_context->TrustStatus .dwErrorStatus ;
10666+
10667+ // Verify chain policy
10668+ auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
10669+ extra_policy_para.cbSize = sizeof (extra_policy_para);
10670+ extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
10671+ auto whost = detail::u8string_to_wstring (host_.c_str ());
10672+ if (server_hostname_verification_) {
10673+ extra_policy_para.pwszServerName =
10674+ const_cast <wchar_t *>(whost.c_str ());
10675+ }
10676+
10677+ auto policy_para = CERT_CHAIN_POLICY_PARA{};
10678+ policy_para.cbSize = sizeof (policy_para);
10679+ policy_para.dwFlags =
10680+ CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
10681+ policy_para.pvExtraPolicyPara = &extra_policy_para;
10682+
10683+ auto policy_status = CERT_CHAIN_POLICY_STATUS{};
10684+ policy_status.cbSize = sizeof (policy_status);
10685+
10686+ result = CertVerifyCertificateChainPolicy (
10687+ CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
10688+ &policy_status);
10689+
10690+ CertFreeCertificateChain (chain_context);
10691+
10692+ if (!result) {
10693+ last_wincrypt_error_ = GetLastError ();
10694+ error = Error::SSLServerVerification;
10695+ return false ;
10696+ }
10697+
10698+ if (policy_status.dwError != 0 ) {
10699+ // Store the specific Windows certificate error code
10700+ last_wincrypt_error_ = policy_status.dwError ;
10701+
10702+ if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
10703+ error = Error::SSLServerHostnameVerification;
10704+ } else {
10705+ error = Error::SSLServerVerification;
10706+ }
10707+ return false ;
10708+ }
10709+ #endif // not _WIN32 || CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1055610710 }
1055710711 }
1055810712
0 commit comments