// Minimal proof of the CRL theory. // // Grab the secure proxy's TLS certificate, then build its chain TWICE on this // same machine -- once WITHOUT revocation checking, once WITH. That flag is // the ONLY variable. If the plain chain is clean (cert is valid & trusted) and // only the revocation chain reports OFFLINE, then the WinHTTP 12188 failure is // 100% a revocation (CRL) problem -- not CA, name, or date. QED. // // Build: cl crlproof.cpp (or the CMake target `crlproof`) #include #include #include #include #pragma comment(lib, "winhttp") #pragma comment(lib, "crypt32") #ifndef WINHTTP_OPTION_SERVER_CERTIFICATE_CONTEXT #define WINHTTP_OPTION_SERVER_CERTIFICATE_CONTEXT 78 #endif // Connect DIRECTLY to the proxy's TLS port (no proxy hop => no revocation by // default) and ignore CA/name/date so the handshake completes, then read back // the certificate the proxy presented. static PCCERT_CONTEXT grabProxyCert() { HINTERNET s = WinHttpOpen(L"crlproof", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); HINTERNET c = WinHttpConnect(s, L"mysecureproxy.duckdns.org", 3129, 0); HINTERNET r = WinHttpOpenRequest(c, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); DWORD ignore = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; WinHttpSetOption(r, WINHTTP_OPTION_SECURITY_FLAGS, &ignore, sizeof(ignore)); WinHttpSendRequest(r, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); PCCERT_CONTEXT cert = NULL; DWORD len = sizeof(cert); WinHttpQueryOption(r, WINHTTP_OPTION_SERVER_CERTIFICATE_CONTEXT, &cert, &len); WinHttpCloseHandle(r); WinHttpCloseHandle(c); WinHttpCloseHandle(s); return cert; // caller frees } static DWORD chainErrorStatus(PCCERT_CONTEXT cert, bool revocation) { CERT_CHAIN_PARA para{}; para.cbSize = sizeof(para); DWORD flags = revocation ? CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT : 0; PCCERT_CHAIN_CONTEXT chain = NULL; if (!CertGetCertificateChain(NULL, cert, NULL, NULL, ¶, flags, NULL, &chain)) return 0xFFFFFFFF; DWORD st = chain->TrustStatus.dwErrorStatus; CertFreeCertificateChain(chain); return st; } int main() { PCCERT_CONTEXT cert = grabProxyCert(); if (!cert) { printf("could not obtain proxy cert (err=%lu)\n", GetLastError()); return 1; } DWORD without = chainErrorStatus(cert, false); DWORD with = chainErrorStatus(cert, true); printf("proxy cert chain dwErrorStatus:\n"); printf(" WITHOUT revocation : 0x%08lX\n", without); printf(" WITH revocation : 0x%08lX\n", with); printf(" (REVOCATION_STATUS_UNKNOWN=0x%08lX IS_OFFLINE_REVOCATION=0x%08lX)\n", (DWORD)CERT_TRUST_REVOCATION_STATUS_UNKNOWN, (DWORD)CERT_TRUST_IS_OFFLINE_REVOCATION); bool proven = (without == 0) && (with & (CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION)); printf("\nCRL theory %s -- cert is valid; only the revocation check fails.\n", proven ? "CONFIRMED" : "NOT confirmed"); CertFreeCertificateContext(cert); return proven ? 0 : 2; }