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.
A sample code that verifies a certificate against Windows system certificate store (for brevity, error checking was removed):
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;
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
.
As suggested by @bartonjs, I've also tried using HCCE_CURRENT_USER
(=NULL
) instead of using own engine created with CertCreateCertificateChainEngine
. It made no difference.
But other apps can connect to my website:
- Users can connect to my website using their webbrowsers (but I'm aware that they have their own trust store, so that is not so surprising to me).
- But users can also connect using
Invoke-WebRequest
in PowerShell. - And my site's certificate is also successfully verified using .NET's
X509Chain.Build
(what I assume is what happens insideInvoke-WebRequest
):
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 does differently, that it can successfully verify the replaced certificate chain?
#define HCCE_CURRENT_USER ((HCERTCHAINENGINE)NULL)
– TriflingHCCE_CURRENT_USER
made no difference. – QuandaryIssued by
section, and verify that it's the sameIssued By
you see from within your C++ code, and it's also the sameIssued by
that you see when connecting from outside the corporate network. globalsign.com/en/blog/how-to-view-ssl-certificate-details ) – Socialize