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

时间:2009-04-08 22:19:35

标签: security ssl wininet

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

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

2 个答案:

答案 0 :(得分:2)

除了之前的回答。 如果您想要使用不受信任的根(例如自签名)手动检查证书,则需要

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));

2)发送数据请求

If(HttpSendRequest(hRequest, strHeaders, strHeaders.GetLength(), data, len)) {

3)只有在数据请求返回后,我们才能查看证书信息并检查指纹。 要获得指纹,我们必须使用来自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; impleCertChainIndex++)
        {
            // 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);
}

4)为什么使用指纹来比较证书? 证书中有三个有趣的字段要比较: 序列号,公钥,指纹。 序列号只是颁发证书的CA选择的唯一号码, 公钥良好的解决方案,但指纹是在完整证书上哈希计算的 (https://security.stackexchange.com/questions/35691/what-is-the-difference-between-serial-number-and-thumbprint

答案 1 :(得分:0)

如果在调用HttpOpenRequest时设置INTERNET_FLAG_SECURE,WinInet将验证返回的证书的域名是否与证书匹配,证书链是否可信。

之后你可以做的事情很少:

  1. 使用INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT的lpszIssuerInfo将返回的域名和证书名称匹配与预期的父证书进行比较。

  2. 从lpszIssuerInfo中解析颁发者名称,并调用CertFindCertificateInStore获取证书上下文指针。

  3. 使用CertGetCertificateChain和证书上下文指针获取并验证证书链,例如比较颁发证书的指纹,而不是我所知的实际证书本身。

  4. 供将来参考,来自MSDN:“http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx”。如果安装了IE8.0,则会有一个新选项公开服务器的证书链。

    INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT 105

    将服务器的证书链上下文检索为重复的PCCERT_CHAIN_CONTEXT。您可以将此重复的上下文传递给任何需要PCCERT_CHAIN_CONTEXT的Crypto API函数。完成证书链上下文后,必须在返回的PCCERT_CHAIN_CONTEXT上调用CertFreeCertificateChain。

    版本:需要Internet Explorer 8.0。