最近在写Windows桌面程序时,需要在https请求中加入证书验证。使用的http库是libcurl+openssl。有了openssl的证书验证功能,只能嵌入CA证书。但是我的程序不方便更新,最好的办法就是用Windows证书库进行验证。这里有两种方式。
遍历Windows信任证书,并将它们添加到证书存储中
使用窗口界面验证证书链
遍历Windows信任证书,并将它们添加到证书存储中
这种方法的缺点是,如果Windows不安装服务器使用的CA证书,身份验证将失败。
void addcertificates for STORE(X509 _ STORE * cert STORE,const char *subSystemName)
{
HCERTSTORE storeHandle =空;
PCCERT _ CONTENT WindowsCertificate = null ptr;
做
{
HCERTSTORE store handle = CertOpenSystemStoreA(NULL,subSystemName);
if(!storeHandle) {
打破;
}
while(windows certificate = certenumcertificates instore(store handle,windowsCertificate)) {
X509 * opensslccertificate = d2i _ X509(null ptr,const _ cast & lt无符号字符const ** >;(& ampwindows证书->;pbCertEncoded),
windows证书->;cbCertEncoded);
if (opensslCertificate) {
X509_STORE_add_cert(certStore,OpenSSLCcertificate);
x509 _ free(OpenSSlceCertificate);
}
}
} while(false);
if (storeHandle) {
CertCloseStore(storeHandle,0);
}
}
int SSLContextFunction(void * curl,void* sslctx,void* userdata)
{
auto certStore = SSL _ CTX _ get _ cert _ store(重新解释_ cast & ltSSL _ CTX * >;(sslctx));
if (certStore) {
addcertificates for store(cert store,“CA”);
addcertificates for store(cert store,“AuthRoot”);
addcertificates for store(cert store," ROOT ");
}
返回CURLE _ OK
}
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1);
curl_easy_setopt(curl,CURLOPT_SSL_CTX_FUNCTION,SSLContextFunction);
一个
2
三
四
五
六
七
八
九
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
使用窗口界面验证证书链
这种方法的优点是即使Windows证书不完整,也可以自动更新,缺点是验证时间可能很长。
libcurl的openssl可以替换为winssl,替换后在物理机上正常运行,但是虚拟机中新安装的Win7显示错误信息:因为撤销服务器离线,撤销功能无法检查撤销。
本来Windows在验证证书的时候,默认会通过网络获取CA的CRL(证书撤销列表),检查证书是否被撤销。我们可以设置不通过libcurl检查CRL(这样做是不安全的)。
curl_easy_setopt(curl,CURLOPT_SSL_OPTIONS,CURLSLOPT _ NO _ REVOKE);
一个
禁用证书吊销检查后出错,显示超时15秒。
为了知道哪一步出错了,我编写了验证证书代码(使用Windows界面),而不是libcurl的默认实现。代码可以参考libcurl、php和chromium,使用cert _ chain _ revolution _ check _ cache _ only可以防止从网络获取CRL。
静态int SSLContextFunction(void * curl,void* sslctx,void* userdata)
{
SSL _ CTX * SSLContext = re interpret _ cast & lt;SSL _ CTX * >;(sslctx);
SSL_CTX_set_verify(sslContext,SSL_VERIFY_PEER,NULL);
SSL _ CTX _ set _ cert _ verify _ callback(SSLContext,sslVerifyCallback,user data);
返回CURLE _ OK
}
静态int SSlverifCallback(X509 _ STORE _ CTX * X509 _ STORE _ CTX,void *arg)
{
BOOL ret = FALSE
PCCERT _ CONTEXT certCtx = nullptr
PCCERT _ CHAIN _ CONTENT certchAIncTx = null ptr;
无符号char * derBuf = nullptr
无符号字符* certNameUtf8 = nullptr
int derLen
# if OPENSSL _ VERSION _ NUMBER & lt0x10100000L
x509 * cert = x509 _ store _ CTX-& gt;cert
#否则
X509 * cert = X509 _ STORE _ CTX _ get0 _ cert(X509 _ STORE _ CTX);
#endif
做
{
/*首先将x509结构转换回一个DER编码的缓冲区,并让Windows将其解码成一个可以使用的形式*/
derLen = i2d_X509(cert,ampderBuf);
if(Derlen & lt;0) {
LOG_ERROR("编码X509证书失败");
打破;
}
certCtx = CertCreateCertificateContext(X509 _ ASN _ ENCODING,derBuf,derLen);
if (certCtx == NULL) {
LOG_ERROR("创建证书上下文失败");
打破;
}
/*接下来从存储中获取相关的证书链*/
CERT _ ENHKEY _ USage ENHKEY USage = { 0 };
CERT _ USage _ MATCH certUsage = { 0 };
CERT _ CHAIN _ PARA CHAIN params = { sizeof(CERT _ CHAIN _ PARA)};
LPSTR用法[] = { szOID_PKIX_KP_SERVER_AUTH,szOID_SERVER_GATED_CRYPTO,szOID _ SGC _ NETSCAPE };
enhkeyusage . cusage identifier = 3;
enhkeyuses . rgpszusageidentifier = uses;
certusage . dwtype = USage _ MATCH _ TYPE _ OR;
certUsage。Usage = enhkeyUsage
链参数。RequestedUsage = certUsage
DWORD CHAIN flags = CERT _ CHAIN _ CACHE _ END _ CERT | CERT _ CHAIN _ REVOLUTION _ CHAIN _ EXCLUDE _ ROOT;
if(!证书证书证书(空,证书发送,空,证书发送->;hCertStore & amp。链参数,链标志,空,和;certChainCtx)) {
LOG_ERROR("获取证书链失败");
打破;
}
if (certChainCtx) {
LOG_INFO("证书链上下文错误状态:%08x,信息状态:%08x ",certChainCtx-& gt;TrustStatus.dwErrorStatus,
certChainCtx->;trust status . dwinfostatus);
}
/*然后根据策略进行验证*/
auto cert name = X509 _ get _ subject _ name(cert);
自动索引= X509 _ NAME _ get _ index _ by _ NID(CertName,NID_commonName,-1);
if(索引& lt0) {
LOG_ERROR("找不到证书CN ");
打破;
}
ASN1 _ STRING _ to _ UTF8(& amp;certNameUtf8,X509 _ NAME _ ENTRY _ get _ data(X509 _ NAME _ get _ ENTRY(certName,index));
STD::wstring Servername;
if(!struils::utf8 ounicode(serverName,(char *)(certNameUtf8)){
LOG_ERROR("无法将证书名称转换为宽字符串"/>
为什么要从网络上获取CTL和根证书?运行certmgr.msc可以发现新安装的Win7根证书非常少。当您遇到系统没有的根证书时,您需要查询证书信任列表并下载根证书。但是在浏览器中手动下载CTL和根证书一点都不慢,很正常。
在调用CertGetCertificateChain时,我抓取了程序转储,发现程序在winhttpgetproviderfurtl中被阻塞。crypt接口最初使用winhttp获取CTL和根证书,在发起请求之前,使用winhttpgetproviderfurtl获取代理信息。
winhttpgetpropoxyforurl为什么一直阻塞?使用IDA分析发现winhttpgetpropoxyforurl会使用RPC调用WPAD服务查询代理信息,然后调用WaitForMultipleObjects等待返回,所以最终分析WPAD服务被阻塞。
Win7默认启动WPAD(WinHTTP Web Proxy Auto-Discovery Service),允许程序自动发现代理服务器。WPAD可以通过域名服务器或DHCP服务器查询代理自动配置文件的位置。关闭互联网选项-连接-局域网设置-自动检测设置或禁用WPAD服务,然后再次运行。发现这是正常的。
为什么WPAD服务受阻?在调用CertGetCertificateChain时,我抓取了WPAD服务转储,发现WPAD会调用gethostname来获取本地主机名,然后调用getaddrinfo来解析,最后阻塞了Nbt_ResolveName。
查数据发现解析本地主机名超时和netbios、dhcp有关。虚拟机下的Windows本地连接会生成一个“特定连接DNS后缀”localdomain,手动生成本地连接的ip地址是正常的。
-
原文:https://blog.csdn.net/xiongya8888/article/details/86419588
版权声明:本文为博主原创文章。请附上博客链接转载!
1.《windows验证 Windows下验证https证书》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《windows验证 Windows下验证https证书》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/fangchan/1261489.html