最近在寫Windows桌面程序時,需要在https請求中加入證書驗(yàn)證。使用的http庫是libcurl+openssl。有了openssl的證書驗(yàn)證功能,只能嵌入CA證書。但是我的程序不方便更新,最好的辦法就是用Windows證書庫進(jìn)行驗(yàn)證。這里有兩種方式。
遍歷Windows信任證書,并將它們添加到證書存儲中
使用窗口界面驗(yàn)證證書鏈
遍歷Windows信任證書,并將它們添加到證書存儲中
這種方法的缺點(diǎn)是,如果Windows不安裝服務(wù)器使用的CA證書,身份驗(yàn)證將失敗。
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
使用窗口界面驗(yàn)證證書鏈
這種方法的優(yōu)點(diǎn)是即使Windows證書不完整,也可以自動更新,缺點(diǎn)是驗(yàn)證時間可能很長。
libcurl的openssl可以替換為winssl,替換后在物理機(jī)上正常運(yùn)行,但是虛擬機(jī)中新安裝的Win7顯示錯誤信息:因?yàn)槌蜂N服務(wù)器離線,撤銷功能無法檢查撤銷。
本來Windows在驗(yàn)證證書的時候,默認(rèn)會通過網(wǎng)絡(luò)獲取CA的CRL(證書撤銷列表),檢查證書是否被撤銷。我們可以設(shè)置不通過libcurl檢查CRL(這樣做是不安全的)。
curl_easy_setopt(curl,CURLOPT_SSL_OPTIONS,CURLSLOPT _ NO _ REVOKE);
一個
禁用證書吊銷檢查后出錯,顯示超時15秒。
為了知道哪一步出錯了,我編寫了驗(yàn)證證書代碼(使用Windows界面),而不是libcurl的默認(rèn)實(shí)現(xiàn)。代碼可以參考libcurl、php和chromium,使用cert _ chain _ revolution _ check _ cache _ only可以防止從網(wǎng)絡(luò)獲取CRL。
靜態(tài)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
}
靜態(tài)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結(jié)構(gòu)轉(zhuǎn)換回一個DER編碼的緩沖區(qū),并讓W(xué)indows將其解碼成一個可以使用的形式*/
derLen = i2d_X509(cert,ampderBuf);
if(Derlen & lt;0) {
LOG_ERROR("編碼X509證書失敗");
打破;
}
certCtx = CertCreateCertificateContext(X509 _ ASN _ ENCODING,derBuf,derLen);
if (certCtx == NULL) {
LOG_ERROR("創(chuàng)建證書上下文失敗");
打破;
}
/*接下來從存儲中獲取相關(guān)的證書鏈*/
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
鏈參數(shù)。RequestedUsage = certUsage
DWORD CHAIN flags = CERT _ CHAIN _ CACHE _ END _ CERT | CERT _ CHAIN _ REVOLUTION _ CHAIN _ EXCLUDE _ ROOT;
if(!證書證書證書(空,證書發(fā)送,空,證書發(fā)送->;hCertStore & amp。鏈參數(shù),鏈標(biāo)志,空,和;certChainCtx)) {
LOG_ERROR("獲取證書鏈?zhǔn)?);
打破;
}
if (certChainCtx) {
LOG_INFO("證書鏈上下文錯誤狀態(tài):%08x,信息狀態(tài):%08x ",certChainCtx-& gt;TrustStatus.dwErrorStatus,
certChainCtx->;trust status . dwinfostatus);
}
/*然后根據(jù)策略進(jìn)行驗(yàn)證*/
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("無法將證書名稱轉(zhuǎn)換為寬字符串"/>
為什么要從網(wǎng)絡(luò)上獲取CTL和根證書?運(yùn)行certmgr.msc可以發(fā)現(xiàn)新安裝的Win7根證書非常少。當(dāng)您遇到系統(tǒng)沒有的根證書時,您需要查詢證書信任列表并下載根證書。但是在瀏覽器中手動下載CTL和根證書一點(diǎn)都不慢,很正常。
在調(diào)用CertGetCertificateChain時,我抓取了程序轉(zhuǎn)儲,發(fā)現(xiàn)程序在winhttpgetproviderfurtl中被阻塞。crypt接口最初使用winhttp獲取CTL和根證書,在發(fā)起請求之前,使用winhttpgetproviderfurtl獲取代理信息。
winhttpgetpropoxyforurl為什么一直阻塞?使用IDA分析發(fā)現(xiàn)winhttpgetpropoxyforurl會使用RPC調(diào)用WPAD服務(wù)查詢代理信息,然后調(diào)用WaitForMultipleObjects等待返回,所以最終分析WPAD服務(wù)被阻塞。
Win7默認(rèn)啟動WPAD(WinHTTP Web Proxy Auto-Discovery Service),允許程序自動發(fā)現(xiàn)代理服務(wù)器。WPAD可以通過域名服務(wù)器或DHCP服務(wù)器查詢代理自動配置文件的位置。關(guān)閉互聯(lián)網(wǎng)選項(xiàng)-連接-局域網(wǎng)設(shè)置-自動檢測設(shè)置或禁用WPAD服務(wù),然后再次運(yùn)行。發(fā)現(xiàn)這是正常的。
為什么WPAD服務(wù)受阻?在調(diào)用CertGetCertificateChain時,我抓取了WPAD服務(wù)轉(zhuǎn)儲,發(fā)現(xiàn)WPAD會調(diào)用gethostname來獲取本地主機(jī)名,然后調(diào)用getaddrinfo來解析,最后阻塞了Nbt_ResolveName。
查數(shù)據(jù)發(fā)現(xiàn)解析本地主機(jī)名超時和netbios、dhcp有關(guān)。虛擬機(jī)下的Windows本地連接會生成一個“特定連接DNS后綴”localdomain,手動生成本地連接的ip地址是正常的。
-
原文:https://blog.csdn.net/xiongya8888/article/details/86419588
版權(quán)聲明:本文為博主原創(chuàng)文章。請附上博客鏈接轉(zhuǎn)載!
1.《windows驗(yàn)證 Windows下驗(yàn)證https證書》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點(diǎn),與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《windows驗(yàn)證 Windows下驗(yàn)證https證書》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進(jìn)行證實(shí),對其原創(chuàng)性、真實(shí)性、完整性、及時性不作任何保證。
3.文章轉(zhuǎn)載時請保留本站內(nèi)容來源地址,http://f99ss.com/fangchan/1261489.html