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

发布于 2024-07-16 04:35:43 字数 227 浏览 5 评论 0原文

我正在尝试对 WinINet 客户端实施手动自签名 SSL 证书验证。 我尝试通过使用 INTERNET_OPTION_SECURITY_CERTIFICATE 或 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT 参数调用 InternetQueryOption 来处理它,但两者都返回服务器证书的一些内部解释,都不允许访问原始证书公钥或至少是指纹。

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

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?...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月朦胧 2024-07-23 04:35:43

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

  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));
  1. 发送数据请求
if (HttpSendRequest(hRequest, strHeaders, strHeaders.GetLength(), data, len)) {
  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);
}
  1. 为什么使用指纹来比较证书?
    证书中有三个有趣的字段需要比较:
    序列号、公钥、指纹。
    序列号只是颁发证书的 CA 选择的唯一编号,
    公钥很好的解决方案,但指纹是通过完整证书计算的哈希值
    https://security.stackexchange.com/questions /35691/序列号和指纹之间的区别是什么

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));
  1. Send data request
if (HttpSendRequest(hRequest, strHeaders, strHeaders.GetLength(), data, len)) {
  1. 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; 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);
}
  1. 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)
幸福丶如此 2024-07-23 04:35:43

如果您在调用 HttpOpenRequest 时设置了 INTERNET_FLAG_SECURE,WinInet 将已经验证返回的证书的域名是否与证书匹配,并且证书链是受信任的。

之后您可以执行以下操作:

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

    使用 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT 的 lpszIssuerInfo 将返回的域名和证书名称

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

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

    使用 CertGetCertificateChain 和证书上下文指针

为了将来参考,来自 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。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文