mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-16 10:49:36 +00:00
1 parent
94b5038eb3
commit
8b4146324f
3 files changed
+247
-25
No files matched your search
@@ -2267,6 +2267,11 @@ public:
|
||||
|
||||
tls::ctx_t tls_context() const;
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
void enable_windows_certificate_verification(bool enabled);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool is_ssl_ = false;
|
||||
#endif
|
||||
@@ -2384,6 +2389,11 @@ public:
|
||||
|
||||
tls::ctx_t tls_context() const { return ctx_; }
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
void enable_windows_certificate_verification(bool enabled);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool create_and_connect_socket(Socket &socket, Error &error) override;
|
||||
bool ensure_socket_connection(Socket &socket, Error &error) override;
|
||||
@@ -2412,6 +2422,11 @@ private:
|
||||
|
||||
std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
bool enable_windows_cert_verification_ = true;
|
||||
#endif
|
||||
|
||||
friend class ClientImpl;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -3680,6 +3695,7 @@ std::string get_cert_issuer_name(cert_t cert);
|
||||
bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans);
|
||||
bool get_cert_validity(cert_t cert, time_t ¬_before, time_t ¬_after);
|
||||
std::string get_cert_serial(cert_t cert);
|
||||
bool get_cert_der(cert_t cert, std::vector<unsigned char> &der);
|
||||
const char *get_sni(const_session_t session);
|
||||
|
||||
// CA store management
|
||||
@@ -7736,6 +7752,96 @@ inline bool match_hostname(const std::string &pattern,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Verify certificate using Windows CertGetCertificateChain API.
|
||||
// This provides real-time certificate validation with Windows Update
|
||||
// integration, independent of the TLS backend (OpenSSL or MbedTLS).
|
||||
inline bool verify_cert_with_windows_schannel(
|
||||
const std::vector<unsigned char> &der_cert, const std::string &hostname,
|
||||
bool verify_hostname, unsigned long &out_error) {
|
||||
if (der_cert.empty()) { return false; }
|
||||
|
||||
out_error = 0;
|
||||
|
||||
// Create Windows certificate context from DER data
|
||||
auto cert_context = CertCreateCertificateContext(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
|
||||
static_cast<DWORD>(der_cert.size()));
|
||||
|
||||
if (!cert_context) {
|
||||
out_error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cert_guard =
|
||||
scope_exit([&] { CertFreeCertificateContext(cert_context); });
|
||||
|
||||
// Setup chain parameters
|
||||
CERT_CHAIN_PARA chain_para = {};
|
||||
chain_para.cbSize = sizeof(chain_para);
|
||||
|
||||
// Build certificate chain with revocation checking
|
||||
PCCERT_CHAIN_CONTEXT chain_context = nullptr;
|
||||
auto chain_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);
|
||||
|
||||
if (!chain_result || !chain_context) {
|
||||
out_error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto chain_guard =
|
||||
scope_exit([&] { CertFreeCertificateChain(chain_context); });
|
||||
|
||||
// Check if chain has errors
|
||||
if (chain_context->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) {
|
||||
out_error = chain_context->TrustStatus.dwErrorStatus;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify SSL policy
|
||||
SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para = {};
|
||||
extra_policy_para.cbSize = sizeof(extra_policy_para);
|
||||
#ifdef AUTHTYPE_SERVER
|
||||
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
|
||||
#endif
|
||||
|
||||
std::wstring whost;
|
||||
if (verify_hostname) {
|
||||
whost = u8string_to_wstring(hostname.c_str());
|
||||
extra_policy_para.pwszServerName = const_cast<wchar_t *>(whost.c_str());
|
||||
}
|
||||
|
||||
CERT_CHAIN_POLICY_PARA policy_para = {};
|
||||
policy_para.cbSize = sizeof(policy_para);
|
||||
#ifdef CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS
|
||||
policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
|
||||
#else
|
||||
policy_para.dwFlags = 0;
|
||||
#endif
|
||||
policy_para.pvExtraPolicyPara = &extra_policy_para;
|
||||
|
||||
CERT_CHAIN_POLICY_STATUS policy_status = {};
|
||||
policy_status.cbSize = sizeof(policy_status);
|
||||
|
||||
if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chain_context,
|
||||
&policy_para, &policy_status)) {
|
||||
out_error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy_status.dwError != 0) {
|
||||
out_error = policy_status.dwError;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace detail
|
||||
#endif // CPPHTTPLIB_SSL_ENABLED
|
||||
|
||||
@@ -14100,6 +14206,13 @@ inline void SSLClient::set_session_verifier(
|
||||
session_verifier_ = std::move(verifier);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
inline void SSLClient::enable_windows_certificate_verification(bool enabled) {
|
||||
enable_windows_cert_verification_ = enabled;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void SSLClient::load_ca_cert_store(const char *ca_cert,
|
||||
std::size_t size) {
|
||||
if (ctx_ && ca_cert && size > 0) {
|
||||
@@ -14251,6 +14364,29 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
// Additional Windows Schannel verification.
|
||||
// This provides real-time certificate validation with Windows Update
|
||||
// integration, working with both OpenSSL and MbedTLS backends.
|
||||
// Skip when a custom CA cert is specified, as the Windows certificate
|
||||
// store would not know about user-provided CA certificates.
|
||||
if (enable_windows_cert_verification_ && ca_cert_file_path_.empty() &&
|
||||
ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) {
|
||||
std::vector<unsigned char> der;
|
||||
if (get_cert_der(server_cert, der)) {
|
||||
unsigned long wincrypt_error = 0;
|
||||
if (!detail::verify_cert_with_windows_schannel(
|
||||
der, host_, server_hostname_verification_, wincrypt_error)) {
|
||||
last_backend_error_ = wincrypt_error;
|
||||
error = Error::SSLServerVerification;
|
||||
output_error_log(error, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
success = true;
|
||||
@@ -14276,6 +14412,16 @@ inline void Client::enable_server_hostname_verification(bool enabled) {
|
||||
cli_->enable_server_hostname_verification(enabled);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && \
|
||||
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
||||
inline void Client::enable_windows_certificate_verification(bool enabled) {
|
||||
if (is_ssl_) {
|
||||
static_cast<SSLClient &>(*cli_).enable_windows_certificate_verification(
|
||||
enabled);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||
const std::string &ca_cert_dir_path) {
|
||||
cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
|
||||
@@ -14729,25 +14875,28 @@ inline bool load_system_certs(ctx_t ctx) {
|
||||
auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: Load from system certificate store
|
||||
// Windows: Load from system certificate store (ROOT and CA)
|
||||
auto store = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
if (!store) return false;
|
||||
|
||||
auto hStore = CertOpenSystemStoreW(NULL, L"ROOT");
|
||||
if (!hStore) return false;
|
||||
|
||||
bool loaded_any = false;
|
||||
PCCERT_CONTEXT pContext = nullptr;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
const unsigned char *data = pContext->pbCertEncoded;
|
||||
auto x509 = d2i_X509(nullptr, &data, pContext->cbCertEncoded);
|
||||
if (x509) {
|
||||
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
||||
X509_free(x509);
|
||||
static const wchar_t *store_names[] = {L"ROOT", L"CA"};
|
||||
for (auto store_name : store_names) {
|
||||
auto hStore = CertOpenSystemStoreW(NULL, store_name);
|
||||
if (!hStore) continue;
|
||||
|
||||
PCCERT_CONTEXT pContext = nullptr;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
const unsigned char *data = pContext->pbCertEncoded;
|
||||
auto x509 = d2i_X509(nullptr, &data, pContext->cbCertEncoded);
|
||||
if (x509) {
|
||||
if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
|
||||
X509_free(x509);
|
||||
}
|
||||
}
|
||||
CertCloseStore(hStore, 0);
|
||||
}
|
||||
CertCloseStore(hStore, 0);
|
||||
return loaded_any;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
@@ -15288,6 +15437,17 @@ inline std::string get_cert_serial(cert_t cert) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
|
||||
if (!cert) return false;
|
||||
auto x509 = static_cast<X509 *>(cert);
|
||||
auto len = i2d_X509(x509, nullptr);
|
||||
if (len < 0) return false;
|
||||
der.resize(static_cast<size_t>(len));
|
||||
auto p = der.data();
|
||||
i2d_X509(x509, &p);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const char *get_sni(const_session_t session) {
|
||||
if (!session) return nullptr;
|
||||
auto ssl = static_cast<SSL *>(const_cast<void *>(session));
|
||||
@@ -16136,17 +16296,20 @@ inline bool load_system_certs(ctx_t ctx) {
|
||||
bool loaded = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Load from Windows certificate store
|
||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
||||
if (hStore) {
|
||||
PCCERT_CONTEXT pContext = nullptr;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
int ret = mbedtls_x509_crt_parse_der(
|
||||
&mctx->ca_chain, pContext->pbCertEncoded, pContext->cbCertEncoded);
|
||||
if (ret == 0) { loaded = true; }
|
||||
// Load from Windows certificate store (ROOT and CA)
|
||||
static const wchar_t *store_names[] = {L"ROOT", L"CA"};
|
||||
for (auto store_name : store_names) {
|
||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, store_name);
|
||||
if (hStore) {
|
||||
PCCERT_CONTEXT pContext = nullptr;
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
int ret = mbedtls_x509_crt_parse_der(
|
||||
&mctx->ca_chain, pContext->pbCertEncoded, pContext->cbCertEncoded);
|
||||
if (ret == 0) { loaded = true; }
|
||||
}
|
||||
CertCloseStore(hStore, 0);
|
||||
}
|
||||
CertCloseStore(hStore, 0);
|
||||
}
|
||||
#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
||||
// Load from macOS Keychain
|
||||
@@ -16786,6 +16949,14 @@ inline std::string get_cert_serial(cert_t cert) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
|
||||
if (!cert) return false;
|
||||
auto crt = static_cast<mbedtls_x509_crt *>(cert);
|
||||
if (!crt->raw.p || crt->raw.len == 0) return false;
|
||||
der.assign(crt->raw.p, crt->raw.p + crt->raw.len);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const char *get_sni(const_session_t session) {
|
||||
if (!session) return nullptr;
|
||||
auto msession = static_cast<const impl::MbedTlsSession *>(session);
|
||||
|
||||
Reference in new issue
Block a user