Skip to content

Commit f97e72c

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 yhirose#1978
1 parent 65ce51a commit f97e72c

File tree

1 file changed

+89
-34
lines changed

1 file changed

+89
-34
lines changed

httplib.h

+89-34
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ using ssize_t = long;
185185
#endif // NOMINMAX
186186

187187
#include <io.h>
188+
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT)
189+
#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
190+
#endif
188191
#include <winsock2.h>
189192
#include <ws2tcpip.h>
190193

@@ -5580,34 +5583,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
55805583
SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
55815584
}
55825585

5583-
#ifdef _WIN32
5584-
// NOTE: This code came up with the following stackoverflow post:
5585-
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
5586-
inline bool load_system_certs_on_windows(X509_STORE *store) {
5587-
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
5588-
if (!hStore) { return false; }
5589-
5590-
auto result = false;
5591-
PCCERT_CONTEXT pContext = NULL;
5592-
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
5593-
nullptr) {
5594-
auto encoded_cert =
5595-
static_cast<const unsigned char *>(pContext->pbCertEncoded);
5596-
5597-
auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5598-
if (x509) {
5599-
X509_STORE_add_cert(store, x509);
5600-
X509_free(x509);
5601-
result = true;
5602-
}
5603-
}
5604-
5605-
CertFreeCertificateContext(pContext);
5606-
CertCloseStore(hStore, 0);
5607-
5608-
return result;
5609-
}
5610-
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5586+
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
56115587
#if TARGET_OS_OSX
56125588
template <typename T>
56135589
using CFObjectPtr =
@@ -5697,7 +5673,7 @@ inline bool load_system_certs_on_macos(X509_STORE *store) {
56975673
return result;
56985674
}
56995675
#endif // TARGET_OS_OSX
5700-
#endif // _WIN32
5676+
#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN && __APPLE__
57015677
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
57025678

57035679
#ifdef _WIN32
@@ -9643,14 +9619,11 @@ inline bool SSLClient::load_certs() {
96439619
}
96449620
} else {
96459621
auto loaded = false;
9646-
#ifdef _WIN32
9647-
loaded =
9648-
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9649-
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9622+
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
96509623
#if TARGET_OS_OSX
96519624
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
96529625
#endif // TARGET_OS_OSX
9653-
#endif // _WIN32
9626+
#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN && __APPLE__
96549627
if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
96559628
}
96569629
});
@@ -9690,12 +9663,14 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
96909663
}
96919664

96929665
if (verification_status == SSLVerifierResponse::NoDecisionMade) {
9666+
#ifndef _WIN32
96939667
verify_result_ = SSL_get_verify_result(ssl2);
96949668

96959669
if (verify_result_ != X509_V_OK) {
96969670
error = Error::SSLServerVerification;
96979671
return false;
96989672
}
9673+
#endif
96999674

97009675
auto server_cert = SSL_get1_peer_certificate(ssl2);
97019676
auto se = detail::scope_exit([&] { X509_free(server_cert); });
@@ -9705,12 +9680,92 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
97059680
return false;
97069681
}
97079682

9683+
#ifdef _WIN32
9684+
// Convert OpenSSL certificate to DER format
9685+
auto der_cert =
9686+
std::vector<unsigned char>(i2d_X509(server_cert, nullptr));
9687+
auto der_cert_data = der_cert.data();
9688+
if (i2d_X509(server_cert, &der_cert_data) < 0) {
9689+
error = Error::SSLServerVerification;
9690+
return false;
9691+
}
9692+
9693+
// Create a certificate context from the DER-encoded certificate
9694+
auto cert_context = CertCreateCertificateContext(
9695+
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
9696+
static_cast<DWORD>(der_cert.size()));
9697+
9698+
if (cert_context == nullptr) {
9699+
error = Error::SSLServerVerification;
9700+
return false;
9701+
}
9702+
9703+
auto chain_para = CERT_CHAIN_PARA{};
9704+
chain_para.cbSize = sizeof(chain_para);
9705+
chain_para.dwUrlRetrievalTimeout = 10 * 1000;
9706+
9707+
auto chain_context = PCCERT_CHAIN_CONTEXT{};
9708+
auto result = CertGetCertificateChain(
9709+
nullptr, cert_context, nullptr, cert_context->hCertStore,
9710+
&chain_para,
9711+
CERT_CHAIN_CACHE_END_CERT |
9712+
CERT_CHAIN_REVOCATION_CHECK_END_CERT |
9713+
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
9714+
nullptr, &chain_context);
9715+
9716+
CertFreeCertificateContext(cert_context);
9717+
9718+
if (!result || chain_context == nullptr) {
9719+
error = Error::SSLServerVerification;
9720+
return false;
9721+
}
9722+
9723+
// Verify chain policy
9724+
auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
9725+
extra_policy_para.cbSize = sizeof(extra_policy_para);
9726+
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
9727+
auto whost = detail::u8string_to_wstring(host_.c_str());
9728+
if (server_hostname_verification_) {
9729+
extra_policy_para.pwszServerName =
9730+
const_cast<wchar_t *>(whost.c_str());
9731+
}
9732+
9733+
auto policy_para = CERT_CHAIN_POLICY_PARA{};
9734+
policy_para.cbSize = sizeof(policy_para);
9735+
policy_para.dwFlags =
9736+
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
9737+
policy_para.pvExtraPolicyPara = &extra_policy_para;
9738+
9739+
auto policy_status = CERT_CHAIN_POLICY_STATUS{};
9740+
policy_status.cbSize = sizeof(policy_status);
9741+
9742+
result = CertVerifyCertificateChainPolicy(
9743+
CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
9744+
&policy_status);
9745+
9746+
CertFreeCertificateChain(chain_context);
9747+
9748+
if (!result) {
9749+
error = Error::SSLServerVerification;
9750+
return false;
9751+
}
9752+
9753+
if (policy_status.dwError != 0) {
9754+
if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
9755+
error = Error::SSLServerHostnameVerification;
9756+
} else {
9757+
error = Error::SSLServerVerification;
9758+
}
9759+
return false;
9760+
}
9761+
#else
97089762
if (server_hostname_verification_) {
97099763
if (!verify_host(server_cert)) {
97109764
error = Error::SSLServerHostnameVerification;
97119765
return false;
97129766
}
97139767
}
9768+
#endif
97149769
}
97159770
}
97169771

0 commit comments

Comments
 (0)