Repository navigation
Make CryptoAPI the only chain verifier when Windows verification is on - #2604
Merged
Merged
Conversation
Every backend loads the Windows ROOT and CA stores, but Windows adds a root to them only when CryptoAPI needs it to build a chain. On a machine that had not needed a root yet, the backend rejected the chain before the CryptoAPI check ran, so Windows never fetched the root (for example OpenSSL error 20 for accounts.spotify.com under Starfield Root G2). With Windows verification enabled, the backend's chain verdict is no longer used. Mbed TLS and wolfSSL skip chain verification during the handshake, and the post-handshake verify result is ignored. The CryptoAPI check becomes mandatory: a leaf that cannot be encoded now fails the connection instead of skipping the check, and the chain must allow server authentication, which the backends used to check. A server certificate verifier works on the backend's chain verification, so when one is set the backend still decides and CryptoAPI only adds its own check, as before. Refs #2596
cert.pem does not match HOST. With CryptoAPI as the chain verifier on Windows, the hostname check now fails before the chain check, so the test saw SSLServerHostnameVerification there. Disable hostname verification so it still fails on the untrusted chain everywhere.
yhirose
force-pushed
the
windows-cryptoapi-sole-verifier
branch
from
October 3, 2026 00:48
f08350b to
13e220e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #2596. Stacked on #2602, and replaces #2603.
Problem
Every TLS backend loads its trust store from the Windows
ROOTandCAstores. Windows does not keep every root it trusts in those stores. It adds a root only when CryptoAPI needs it to build a chain.Starfield Root Certificate Authority - G2is one example. On a machine that has not needed it yet,accounts.spotify.comfails like this with OpenSSL:The backend rejects the chain first, so the CryptoAPI check, which would make Windows fetch the root, never runs.
CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATEpromises this automatic update, but it has not worked since the CryptoAPI check started running only after the backend's own verification succeeded.Fix
When Windows certificate verification is enabled, CryptoAPI is the only chain verifier for every backend, as in Go, .NET and curl with Schannel:
CertGetCertificateChain()now requestsszOID_PKIX_KP_SERVER_AUTH. The backends checked the extended key usage, whileCERT_CHAIN_POLICY_SSLdoes not. This also rejects roots that Windows trusts only for other purposes.Hostname verification is unchanged: the backend's
verify_hostname()still runs before the CryptoAPI check.When a server certificate verifier is set with
set_server_certificate_verifier(), the backend still verifies the chain and CryptoAPI adds its own check, as before. The verifier works on the backend's verification result (preverify_ok). With a custom CA, Windows verification stays disabled, as before.Behavior changes with Windows verification enabled
incomplete-chain.badssl.com).ssl_backend_error()holds the CryptoAPI status instead of the backend's error. On Mbed TLS and wolfSSL,ssl_error()is now 0 for these failures.SSLServerHostnameVerificationinstead ofSSLServerVerification.CertificateAcceptednow skips chain verification entirely, as it already did on OpenSSL.Verification
All checks ran on a
windows-latestGitHub Actions runner, comparing #2602 with this PR on OpenSSL 3.6, Mbed TLS 3.6 and wolfSSL 5.8.4.accounts.spotify.com, wolfSSL withwww.godaddy.com)untrusted-root,self-signed,expiredon badssl.comwrong.host.badssl.comSSLServerHostnameVerificationon all backendsincomplete-chain.badssl.comclientAuthEKU, under a root added to the Windows storeCERT_TRUST_IS_NOT_VALID_FOR_USAGEAdded
SSLClientTest.WindowsCertificateVerification_RejectsUntrustedRoot. It runs offline on Windows CI and checks that a self-signed certificate is rejected by CryptoAPI, withCERT_TRUST_IS_UNTRUSTED_ROOTin the error.