My C++ application (WinSCP) connects to my server to check for new versions. The HTTPS code uses neon and OpenSSL libraries. I’m verifying the TLS certificate using CertVerifyCertificateChainPolicy
function. Sample code:
const CERT_CONTEXT * CertContext =
CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, Certificate, Len);
CERT_CHAIN_PARA ChainPara;
memset(&ChainPara, 0, sizeof(ChainPara));
ChainPara.cbSize = sizeof(ChainPara);
CERT_CHAIN_ENGINE_CONFIG ChainConfig;
memset(&ChainConfig, 0, sizeof(ChainConfig));
ChainConfig.cbSize = sizeof(ChainConfig);
ChainConfig.dwFlags = CERT_CHAIN_CACHE_END_CERT;
HCERTCHAINENGINE ChainEngine;
CertCreateCertificateChainEngine(&ChainConfig, &ChainEngine);
const CERT_CHAIN_CONTEXT * ChainContext = NULL;
CertGetCertificateChain(
ChainEngine, CertContext, NULL, NULL, &ChainPara,
CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
NULL, &ChainContext);
CERT_CHAIN_POLICY_PARA PolicyPara;
PolicyPara.cbSize = sizeof(PolicyPara);
CERT_CHAIN_POLICY_STATUS PolicyStatus;
PolicyStatus.cbSize = sizeof(PolicyStatus);
CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, ChainContext, &PolicyPara, &PolicyStatus);
int PolicyError = PolicyStatus.dwError;
(for brevity, error checking was removed)
This works. But I’ve got few reports that the verification fails in corporate environments. The problem seems to happens, when the certificate chain is replaced by the corporate firewall. The PolicyStatus.dwError
is set to 800B0109
= CERT_E_UNTRUSTEDROOT
.
But other apps can connect to my website:
- Users can connect to my website using their webbrowsers.
- They also can connect using
Invoke-WebRequest
in PowerShell. - My site’s certificate is also successfully verified using .NET’s
X509Chain.Build
:
X509Chain chain = new();
chain.Build(sslStream.RemoteCertificate);
// chain.ChainStatus is now empty => certificate is valid
When I’ve checked the .NET code for X509Chain.Build
, it seems to be doing something very similar to my C++ code. But there must be some subtle difference I cannot identify.
Does anyone know what .NET/webbrowsers do differently, that it can successfully verify the replaced certificate chain?