Manually validate server certificate in WinINet

后端 未结 2 1607
时光取名叫无心
时光取名叫无心 2021-01-15 12:29

I\'m trying to implement manual self-signed SSL certificate validation to a WinINet client. I tried to approach it by calling InternetQueryOption with IN

2条回答
  •  甜味超标
    2021-01-15 12:41

    Additionaly to previous answer. If you want to manualy check certificates with untrusted root(self signed for example), you need to

    1) Set flags to ignore untrusted certificates

    // 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) Send data request

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

    3) And only after data request return we can view certificate info and check thumbprint. To get thumbprint we must use methods from cryptapi, so need to #include "WinCrypt.h" and add crypt32.lib to linker

    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; icChain; 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) Why use thumbprint to compare certificate? There three interesting fields in certificate to compare: Serial number, Public key, Thumbprint. Serial number is just unique number chosen by the CA which issued the certificate, public key good solution, but thumbprint is hash computed over the complete certificate (https://security.stackexchange.com/questions/35691/what-is-the-difference-between-serial-number-and-thumbprint)

提交回复
热议问题