在 WinINet 中手动验证服务器证书

gal*_*ets 2 security ssl wininet

我正在尝试对 WinINet 客户端实施手动自签名 SSL 证书验证。我尝试通过使用 或 参数调用来接近它InternetQueryOptionINTERNET_OPTION_SECURITY_CERTIFICATEINTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT两者都返回服务器证书的一些内部解释,没有一个允许访问原始证书公钥或至少是thumbprimnt。

我该如何验证证书?...

小智 5

对之前答案的补充。如果您想手动检查不受信任的根证书(例如自签名),您需要

  1. 设置标志以忽略不受信任的证书
// Open request before
HINTERNET hRequest = HttpOpenRequest(hConnect, _T("POST"), action, NULL, NULL, NULL, dwFlags, 0);
if (!hRequest) return GetLastError();

// set ignore options to request
DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);
InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, (LPVOID)&dwFlags, &dwBuffLen);
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags));
Run Code Online (Sandbox Code Playgroud)
  1. 发送数据请求
if (HttpSendRequest(hRequest, strHeaders, strHeaders.GetLength(), data, len)) {
Run Code Online (Sandbox Code Playgroud)
  1. 只有在数据请求返回后我们才能查看证书信息并检查指纹。要获取指纹,我们必须使用 cryptapi 中的方法,因此需要 #include“WinCrypt.h” 并将 crypt32.lib 添加到链接器
PCCERT_CHAIN_CONTEXT CertCtx=NULL;
DWORD cbCertSize = sizeof(&CertCtx);

// Get certificate chain information
if (InternetQueryOption(hRequest, INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT, (LPVOID)&CertCtx, &cbCertSize))
{
    PCCERT_CHAIN_CONTEXT pChainContext=CertCtx;

    CERT_SIMPLE_CHAIN *simpleCertificateChainWithinContext = NULL;
    for (int i=0; i<pChainContext->cChain; i++)
    {
        simpleCertificateChainWithinContext=pChainContext->rgpChain[i];
        
        // We can check any certificates from chain, but if selfsigned it will be single
        for (int simpleCertChainIndex = 0; simpleCertChainIndex < simpleCertificateChainWithinContext->cElement; simpleCertChainIndex++)
        {
            // get the CertContext from the array
            PCCERT_CONTEXT pCertContext = simpleCertificateChainWithinContext->rgpElement[simpleCertChainIndex]->pCertContext;
            
            // Public key can be getted from
            //  (((*((*pCertContext).pCertInfo)).SubjectPublicKeyInfo).PublicKey).pbData
            // but better to use thumbprint to check

            // CERT_HASH_PROP_ID - is a thumbprint
            BYTE thumbprint[1024];
            DWORD len = 1024;
            if (CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, thumbprint, &len)) {
                //
                // !!! HERE WE CAN CHECK THUMPRINT WITH TRUSTED(PREVIOUSLY SAVED)
                // and return error, or accept request output.
                //
            }
        }
    }
    
    // important! Free the CertCtx
    CertFreeCertificateChain(CertCtx);
}
Run Code Online (Sandbox Code Playgroud)
  1. 为什么要使用指纹来比对证书?证书中有三个有趣的字段需要比较:序列号、公钥、指纹。序列号只是颁发证书的 CA 选择的唯一编号,公钥是一个很好的解决方案,但指纹是通过完整证书进行哈希计算的(https://security.stackexchange.com/questions/35691/what-is-the-序列号和指纹之间的差异