You've already forked cpp-httplib
Default using Windows Schannel for SSL/TLS certificate verification on Windows
This commit adds support for using Windows Schannel (Windows certificate store) for SSL/TLS certificate verification instead of OpenSSL's verification on Windows. This provides automatic root certificate updates from Windows Update. Changes: - Add CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE macro to optionally disable this feature and use OpenSSL verification - Add HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE CMake option (default ON) - Add wincrypt_error() and wincrypt_chain_error() methods to Result class for Windows-specific certificate error information - Modify SSLClient::initialize_ssl() to use Windows CertGetCertificateChain() and CertVerifyCertificateChainPolicy() APIs for certificate verification - Update tests to handle both Windows Schannel and OpenSSL verification paths - Update README.md with documentation for Windows certificate verification Closes #1978 Based on PR #2116 by @solarispika
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
* HTTPLIB_REQUIRE_ZSTD (default off)
|
||||
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
|
||||
* HTTPLIB_USE_NON_BLOCKING_GETADDRINFO (default on)
|
||||
* HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE (default on)
|
||||
* HTTPLIB_COMPILE (default off)
|
||||
* HTTPLIB_INSTALL (default on)
|
||||
* HTTPLIB_SHARED (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
|
||||
@@ -110,6 +111,7 @@ option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system cer
|
||||
option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON)
|
||||
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
|
||||
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
|
||||
option(HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE "Enable automatic root certificates update on Windows." ON)
|
||||
# Defaults to static library but respects standard BUILD_SHARED_LIBS if set
|
||||
include(CMakeDependentOption)
|
||||
cmake_dependent_option(HTTPLIB_SHARED "Build the library as a shared library instead of static. Has no effect if using header-only."
|
||||
@@ -296,6 +298,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
||||
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
|
||||
$<$<BOOL:${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}>:CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO>
|
||||
$<$<AND:$<PLATFORM_ID:Windows>,$<NOT:$<BOOL:${HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE}>>>:CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE>
|
||||
)
|
||||
|
||||
# CMake configuration files installation directory
|
||||
|
||||
77
README.md
77
README.md
@@ -113,13 +113,26 @@ if (!res) {
|
||||
break;
|
||||
|
||||
case httplib::Error::SSLServerVerification:
|
||||
std::cout << "SSL verification failed, X509 error: "
|
||||
<< res.ssl_openssl_error() << std::endl;
|
||||
std::cout << "SSL verification failed" << std::endl;
|
||||
#if defined(_WIN32) && !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// On Windows with Schannel, check Windows certificate errors
|
||||
std::cout << " Windows error: 0x" << std::hex << res.wincrypt_error()
|
||||
<< ", chain status: 0x" << res.wincrypt_chain_error() << std::endl;
|
||||
#else
|
||||
// On other platforms, check OpenSSL errors
|
||||
std::cout << " X509 error: " << res.ssl_openssl_error() << std::endl;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case httplib::Error::SSLServerHostnameVerification:
|
||||
std::cout << "SSL hostname verification failed, X509 error: "
|
||||
<< res.ssl_openssl_error() << std::endl;
|
||||
std::cout << "SSL hostname verification failed" << std::endl;
|
||||
#if defined(_WIN32) && !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// On Windows with Schannel, check Windows certificate errors
|
||||
std::cout << " Windows error: 0x" << std::hex << res.wincrypt_error() << std::endl;
|
||||
#else
|
||||
// On other platforms, check OpenSSL errors
|
||||
std::cout << " X509 error: " << res.ssl_openssl_error() << std::endl;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -128,6 +141,62 @@ if (!res) {
|
||||
}
|
||||
```
|
||||
|
||||
For a simpler platform-agnostic approach, you can check which error field is non-zero:
|
||||
|
||||
```c++
|
||||
auto res = cli.Get("/");
|
||||
if (!res) {
|
||||
if (res.error() == httplib::Error::SSLServerVerification) {
|
||||
std::cout << "Certificate verification failed!" << std::endl;
|
||||
|
||||
// Check which backend reported the error
|
||||
if (res.ssl_openssl_error() != 0) {
|
||||
// OpenSSL reported the error (Linux, macOS, or Windows with Schannel disabled)
|
||||
std::cout << "OpenSSL error: " << res.ssl_openssl_error() << std::endl;
|
||||
}
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#if defined(_WIN32) && !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
else if (res.wincrypt_error() != 0) {
|
||||
// Windows Schannel reported the error
|
||||
std::cout << "Windows error: 0x" << std::hex << res.wincrypt_error() << std::endl;
|
||||
std::cout << "Chain status: 0x" << std::hex << res.wincrypt_chain_error() << std::endl;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Windows Certificate Verification
|
||||
|
||||
On Windows, by default, cpp-httplib uses Windows Schannel for certificate verification instead of OpenSSL's verification. This provides automatic root certificate updates from Windows Update.
|
||||
|
||||
**When certificate verification fails on Windows:**
|
||||
- OpenSSL still handles the TLS handshake
|
||||
- Windows Schannel performs certificate verification
|
||||
- Check `wincrypt_error()` and `wincrypt_chain_error()` for diagnostic information
|
||||
- The `ssl_openssl_error()` field will be 0 for certificate verification errors
|
||||
|
||||
**Windows-specific error codes** (`wincrypt_error()`) include:
|
||||
- `CERT_E_EXPIRED` (0x800B0101) - Certificate has expired
|
||||
- `CERT_E_UNTRUSTEDROOT` (0x800B0109) - Certificate chain to untrusted root
|
||||
- `CERT_E_CN_NO_MATCH` (0x800B010F) - Certificate CN doesn't match hostname
|
||||
- `CERT_E_REVOKED` (0x800B010C) - Certificate has been revoked
|
||||
- `CERT_E_CHAINING` (0x800B010A) - Error building certificate chain
|
||||
|
||||
**Chain trust status** (`wincrypt_chain_error()`) provides additional details:
|
||||
- `CERT_TRUST_IS_NOT_TIME_VALID` (0x00000001) - Certificate expired
|
||||
- `CERT_TRUST_IS_REVOKED` (0x00000004) - Certificate revoked
|
||||
- `CERT_TRUST_IS_NOT_SIGNATURE_VALID` (0x00000008) - Invalid signature
|
||||
- `CERT_TRUST_IS_UNTRUSTED_ROOT` (0x00000020) - Untrusted root
|
||||
|
||||
To disable Windows automatic certificate updates and use OpenSSL verification:
|
||||
```cmake
|
||||
set(HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE OFF)
|
||||
```
|
||||
|
||||
Or define `CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE` before including httplib.h.
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
|
||||
162
httplib.h
162
httplib.h
@@ -221,6 +221,10 @@ using ssize_t = __int64;
|
||||
#endif // NOMINMAX
|
||||
|
||||
#include <io.h>
|
||||
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
@@ -1349,6 +1353,17 @@ public:
|
||||
: res_(std::move(res)), err_(err),
|
||||
request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
|
||||
ssl_openssl_error_(ssl_openssl_error) {}
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
|
||||
int ssl_error, unsigned long ssl_openssl_error,
|
||||
unsigned long wincrypt_error, unsigned long wincrypt_chain_error)
|
||||
: res_(std::move(res)), err_(err),
|
||||
request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
|
||||
ssl_openssl_error_(ssl_openssl_error), wincrypt_error_(wincrypt_error),
|
||||
wincrypt_chain_error_(wincrypt_chain_error) {}
|
||||
#endif
|
||||
#endif
|
||||
// Response
|
||||
operator bool() const { return res_ != nullptr; }
|
||||
@@ -1369,6 +1384,14 @@ public:
|
||||
int ssl_error() const { return ssl_error_; }
|
||||
// OpenSSL Error
|
||||
unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// Windows Certificate Error (from GetLastError or policy_status.dwError)
|
||||
unsigned long wincrypt_error() const { return wincrypt_error_; }
|
||||
// Windows Certificate Chain Trust Status (from TrustStatus.dwErrorStatus)
|
||||
unsigned long wincrypt_chain_error() const { return wincrypt_chain_error_; }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Request Headers
|
||||
@@ -1387,6 +1410,12 @@ private:
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
int ssl_error_ = 0;
|
||||
unsigned long ssl_openssl_error_ = 0;
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
unsigned long wincrypt_error_ = 0;
|
||||
unsigned long wincrypt_chain_error_ = 0;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1706,6 +1735,12 @@ protected:
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
int last_ssl_error_ = 0;
|
||||
unsigned long last_openssl_error_ = 0;
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
unsigned long last_wincrypt_error_ = 0;
|
||||
unsigned long last_wincrypt_chain_error_ = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -6310,6 +6345,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
// NOTE: This code came up with the following stackoverflow post:
|
||||
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
|
||||
inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||
@@ -6336,6 +6372,7 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
|
||||
template <typename T>
|
||||
using CFObjectPtr =
|
||||
@@ -8954,8 +8991,19 @@ inline Result ClientImpl::send_(Request &&req) {
|
||||
auto error = Error::Success;
|
||||
auto ret = send(req, *res, error);
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
return Result{ret ? std::move(res) : nullptr,
|
||||
error,
|
||||
std::move(req.headers),
|
||||
last_ssl_error_,
|
||||
last_openssl_error_,
|
||||
last_wincrypt_error_,
|
||||
last_wincrypt_chain_error_};
|
||||
#else
|
||||
return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
|
||||
last_ssl_error_, last_openssl_error_};
|
||||
#endif
|
||||
#else
|
||||
return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
|
||||
#endif
|
||||
@@ -9541,8 +9589,19 @@ inline Result ClientImpl::send_with_content_provider_and_receiver(
|
||||
std::move(content_receiver), error);
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
return Result{std::move(res),
|
||||
error,
|
||||
std::move(req.headers),
|
||||
last_ssl_error_,
|
||||
last_openssl_error_,
|
||||
last_wincrypt_error_,
|
||||
last_wincrypt_chain_error_};
|
||||
#else
|
||||
return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
|
||||
last_openssl_error_};
|
||||
#endif
|
||||
#else
|
||||
return Result{std::move(res), error, std::move(req.headers)};
|
||||
#endif
|
||||
@@ -11348,8 +11407,10 @@ inline bool SSLClient::load_certs() {
|
||||
} else {
|
||||
auto loaded = false;
|
||||
#ifdef _WIN32
|
||||
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
loaded =
|
||||
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
||||
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
|
||||
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
|
||||
#endif // _WIN32
|
||||
@@ -11396,6 +11457,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
||||
}
|
||||
|
||||
if (verification_status == SSLVerifierResponse::NoDecisionMade) {
|
||||
#if !defined(_WIN32) || \
|
||||
defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
verify_result_ = SSL_get_verify_result(ssl2);
|
||||
|
||||
if (verify_result_ != X509_V_OK) {
|
||||
@@ -11404,6 +11467,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
||||
output_error_log(error, nullptr);
|
||||
return false;
|
||||
}
|
||||
#endif // !_WIN32 ||
|
||||
// CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
|
||||
auto server_cert = SSL_get1_peer_certificate(ssl2);
|
||||
auto se = detail::scope_exit([&] { X509_free(server_cert); });
|
||||
@@ -11415,6 +11480,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) || \
|
||||
defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
if (server_hostname_verification_) {
|
||||
if (!verify_host(server_cert)) {
|
||||
last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
|
||||
@@ -11423,6 +11490,101 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else // _WIN32 &&
|
||||
// !CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
// Windows Schannel verification path - clear OpenSSL errors since
|
||||
// we're not using OpenSSL for certificate verification
|
||||
last_openssl_error_ = 0;
|
||||
|
||||
// Convert OpenSSL certificate to DER format
|
||||
auto der_cert =
|
||||
std::vector<unsigned char>(i2d_X509(server_cert, nullptr));
|
||||
auto der_cert_data = der_cert.data();
|
||||
if (i2d_X509(server_cert, &der_cert_data) < 0) {
|
||||
error = Error::SSLServerVerification;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a certificate context from the DER-encoded certificate
|
||||
auto cert_context = CertCreateCertificateContext(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
|
||||
static_cast<DWORD>(der_cert.size()));
|
||||
|
||||
if (cert_context == nullptr) {
|
||||
last_wincrypt_error_ = GetLastError();
|
||||
error = Error::SSLServerVerification;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto chain_para = CERT_CHAIN_PARA{};
|
||||
chain_para.cbSize = sizeof(chain_para);
|
||||
chain_para.dwUrlRetrievalTimeout = 10 * 1000;
|
||||
|
||||
auto chain_context = PCCERT_CHAIN_CONTEXT{};
|
||||
auto result = CertGetCertificateChain(
|
||||
nullptr, cert_context, nullptr, cert_context->hCertStore,
|
||||
&chain_para,
|
||||
CERT_CHAIN_CACHE_END_CERT |
|
||||
CERT_CHAIN_REVOCATION_CHECK_END_CERT |
|
||||
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
|
||||
nullptr, &chain_context);
|
||||
|
||||
CertFreeCertificateContext(cert_context);
|
||||
|
||||
if (!result || chain_context == nullptr) {
|
||||
if (!result) { last_wincrypt_error_ = GetLastError(); }
|
||||
error = Error::SSLServerVerification;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Capture detailed chain trust status before using the chain
|
||||
last_wincrypt_chain_error_ =
|
||||
chain_context->TrustStatus.dwErrorStatus;
|
||||
|
||||
// Verify chain policy
|
||||
auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
|
||||
extra_policy_para.cbSize = sizeof(extra_policy_para);
|
||||
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
|
||||
auto whost = detail::u8string_to_wstring(host_.c_str());
|
||||
if (server_hostname_verification_) {
|
||||
extra_policy_para.pwszServerName =
|
||||
const_cast<wchar_t *>(whost.c_str());
|
||||
}
|
||||
|
||||
auto policy_para = CERT_CHAIN_POLICY_PARA{};
|
||||
policy_para.cbSize = sizeof(policy_para);
|
||||
policy_para.dwFlags =
|
||||
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
|
||||
policy_para.pvExtraPolicyPara = &extra_policy_para;
|
||||
|
||||
auto policy_status = CERT_CHAIN_POLICY_STATUS{};
|
||||
policy_status.cbSize = sizeof(policy_status);
|
||||
|
||||
result = CertVerifyCertificateChainPolicy(
|
||||
CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
|
||||
&policy_status);
|
||||
|
||||
CertFreeCertificateChain(chain_context);
|
||||
|
||||
if (!result) {
|
||||
last_wincrypt_error_ = GetLastError();
|
||||
error = Error::SSLServerVerification;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy_status.dwError != 0) {
|
||||
// Store the specific Windows certificate error code
|
||||
last_wincrypt_error_ = policy_status.dwError;
|
||||
|
||||
if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
|
||||
error = Error::SSLServerHostnameVerification;
|
||||
} else {
|
||||
error = Error::SSLServerVerification;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // !_WIN32 ||
|
||||
// CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
test/test.cc
27
test/test.cc
@@ -8394,14 +8394,19 @@ TEST(SSLClientTest, ServerCertificateVerificationError_Online) {
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::SSLServerVerification, res.error());
|
||||
|
||||
// For SSL server verification errors, ssl_error should be 0, only
|
||||
// ssl_openssl_error should be set
|
||||
// For SSL server verification errors, ssl_error should be 0
|
||||
EXPECT_EQ(0, res.ssl_error());
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// On Windows with Schannel, check wincrypt_error instead of ssl_openssl_error
|
||||
EXPECT_NE(0UL, res.wincrypt_error());
|
||||
#else
|
||||
// Verify OpenSSL error is captured for SSLServerVerification
|
||||
// This occurs when SSL_get_verify_result() returns a verification failure
|
||||
EXPECT_EQ(static_cast<unsigned long>(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT),
|
||||
res.ssl_openssl_error());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerHostnameVerificationError_Online) {
|
||||
@@ -8416,14 +8421,19 @@ TEST(SSLClientTest, ServerHostnameVerificationError_Online) {
|
||||
|
||||
EXPECT_EQ(Error::SSLServerHostnameVerification, res.error());
|
||||
|
||||
// For SSL hostname verification errors, ssl_error should be 0, only
|
||||
// ssl_openssl_error should be set
|
||||
// For SSL hostname verification errors, ssl_error should be 0
|
||||
EXPECT_EQ(0, res.ssl_error());
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// On Windows with Schannel, check wincrypt_error instead of ssl_openssl_error
|
||||
EXPECT_NE(0UL, res.wincrypt_error());
|
||||
#else
|
||||
// Verify OpenSSL error is captured for SSLServerHostnameVerification
|
||||
// This occurs when verify_host() fails due to hostname mismatch
|
||||
EXPECT_EQ(static_cast<unsigned long>(X509_V_ERR_HOSTNAME_MISMATCH),
|
||||
res.ssl_openssl_error());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
|
||||
@@ -8799,14 +8809,19 @@ TEST(SSLClientServerTest, ClientCertMissing) {
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::SSLServerVerification, res.error());
|
||||
|
||||
// For SSL server verification errors, ssl_error should be 0, only
|
||||
// ssl_openssl_error should be set
|
||||
// For SSL server verification errors, ssl_error should be 0
|
||||
EXPECT_EQ(0, res.ssl_error());
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// On Windows with Schannel, check wincrypt_error instead of ssl_openssl_error
|
||||
EXPECT_NE(0UL, res.wincrypt_error());
|
||||
#else
|
||||
// Verify OpenSSL error is captured for SSLServerVerification
|
||||
// Note: This test may have different error codes depending on the exact
|
||||
// verification failure
|
||||
EXPECT_NE(0UL, res.ssl_openssl_error());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
|
||||
Reference in New Issue
Block a user