使用 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 的客户端身份验证(证书 + 私钥)的主要内容,如果未能解决你的问题,请参考以下文章