使用 WinInet 的客户端身份验证(证书 + 私钥)

Posted

技术标签:

【中文标题】使用 WinInet 的客户端身份验证(证书 + 私钥)【英文标题】:Client authentication (certificat + private key) using WinInet 【发布时间】:2019-03-22 10:47:01 【问题描述】: 这是我的 previous question 的演变,它是关于 WinHttp 的。 我希望这是正确的做法...

我正在尝试通过 https 与使用 WinInet(来自 Win32 API)的服务器进行通信。

这是一个非常简约的代码:

HINTERNET ses = InternetOpen("test",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0) ;
HINTERNET con = InternetOpenUrl( ses,"https://***.com",NULL,0,0,0 ) ;
DWORD read ;
char  str [3000] ;
InternetReadFile( con,reinterpret_cast<void*>( str ),sizeof( str )-1,&read ) ;
str[read] = 0 ;
cout << &str[0] ;
InternetCloseHandle( con ) ;
InternetCloseHandle( ses ) ;

只要我与“经典”https 服务器(如 ***.com)通信,一切都会顺利进行。问题是当我尝试与请求客户端身份验证的服务器通信时。

我有 3 个 .pem 文件:我的客户端的证书和私钥,以及验证我的客户端证书的根证书(即长度为 2 的证书链)。

有关信息,我可以使用此 cULR 命令行连接我的服务器:

curl https://my.server --cert Client_cert.pem --key Client_key.pem --cacert Root_cert.pem

这是有可能的证据!

阅读 WinInet 文档,我发现了一个名为“Handling Authentication”的页面,但都是关于用户名:密码的,而没有关于证书的。

我发现我必须使用 Crypt32 库:我必须使用 CertCreateCertificateContext 创建证书上下文,然后将其插入证书存储区,然后使用该存储区进行连接... 好吧,我必须承认我会很高兴找到一个好的教程或一些代码示例! 顺便说一句,我不知道如何将我的私钥插入那些东西......

提前致谢!

【问题讨论】:

【参考方案1】:

您必须使用INTERNET_OPTION_CLIENT_CERT_CONTEXT 将您的证书传递给WinInet,并调用InternetSetOption():

INTERNET_OPTION_CLIENT_CERT_CONTEXT

84

InternetQueryOption 不支持此标志。 lpBuffer 参数必须是指向 CERT_CONTEXT 结构的指针,而不是 指向 CERT_CONTEXT 指针的指针。如果应用程序收到 ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,它必须调用 InternetErrorDlg 或在重试之前使用 InternetSetOption 提供证书 要求。然后调用 CertDuplicateCertificateContext 以便 传递的证书上下文可以由 应用。

所以,例如:

InternetSetOption(hI,INTERNET_OPTION_CLIENT_CERT_CONTEXT,(void*)cert,sizeof(CERT_CONTEXT));

顺便说一句,curl 调用不是很好的例子,因为 curl 使用了使用 OpenSSL 的 libcurl。

【讨论】:

Thx,我已经阅读了该页面...棘手的部分是我必须构建一个包含证书存储句柄的 CERT_CONTEXT:我必须先构建它吗?那么客户的私钥呢?以及如何检查证书链?我必须自己做吗? 这取决于您在哪里拥有此证书。它在 PFX 文件中吗? docs.microsoft.com/en-us/windows/desktop/api/wincrypt/… - PFXImportStore。然后您可以 CertEnumCertificatesInStore 循环遍历所有证书。私钥存在时会在需要时自动包含在内(例如,您可能会收到 PIN 请求提示)。另见***.com/questions/6307886/… 我的 2 个证书 + 1 个私钥存储在 .pem 文件中。而且我的代码应该静默运行,因此无法请求 PIN... 如果你有私钥和公钥,你用 pvk2pfx 工具把它保存在一个 PFX 中,然后导入它并与我说的功能一起使用。此外,您需要一份证书。链(如果有的话)将从服务器验证,而不是您。如果这不是由您的服务器知道的证书颁发机构签名的密钥,那么您将提供的链无论如何都是无用的。如果是您自己的组织,那么服务器将已经知道根证书。 谢谢迈克尔!最后,我设法从我的 2 个证书 + 私钥构建了一个 PFX。我使用PFXImportCertStore 导入它,但现在我有一个全新的HCERTSTORE,我不知道如何将它传递 到我的https 连接...它没有使用InternetSetOption ...我没有看到任何将HCERTSTORE 作为参数的 Internet 或 Http 函数。

以上是关于使用 WinInet 的客户端身份验证(证书 + 私钥)的主要内容,如果未能解决你的问题,请参考以下文章

Wininet - 如何下载和验证 SSL 证书

wininet如何处理cookies

集成 Windows 身份验证 Wininet

如何使用 wininet.dll 对代理进行身份验证?

下次我使用相同的连接句柄调用 WinInet 调用 HttpSendRequest 时,它不会传递身份验证信息

使用客户端证书的智能卡身份验证