Manually validate server certificate in WinINet

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

I'm trying to implement manual self-signed SSL certificate validation to a WinINet client. I tried to approach it by calling InternetQueryOption with INTERNET_OPTION_SECURITY_CERTIFICATE or INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT parameters, but both return some internal interpretation of server's certificate, none allows accessing raw certificate public key or at least thumbprimnt.

How am I supposed to validate certificate?...

回答1:

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



回答2:

WinInet will already validate returned certificate's domain name matches the certificate and the certificate chain is trusted if you set INTERNET_FLAG_SECURE when calling HttpOpenRequest.

Few things you can do afterwards:

  1. Use INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT's lpszIssuerInfo to compare returned domain name and certificate name match to parent certificate that is expected.

  2. Parse out Issuer name from lpszIssuerInfo and call to CertFindCertificateInStore to get certificate context pointer.

  3. Get and validate certificate chain using CertGetCertificateChain and certificate context pointer, such as comparing thumbprints of issuing certificates, but not the actual certificate itself to my knowledge.

For future reference, from MSDN: "http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx". If IE8.0 is installed, there is a new option that exposes server's certificate chain.

INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT 105

Retrieves the server’s certificate-chain context as a duplicated PCCERT_CHAIN_CONTEXT. You may pass this duplicated context to any Crypto API function which takes a PCCERT_CHAIN_CONTEXT. You must call CertFreeCertificateChain on the returned PCCERT_CHAIN_CONTEXT when you are done with the certificate-chain context.

Version: Requires Internet Explorer 8.0.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!