最近在寫乙個windows桌面程式需要給https請求加上證書驗證,使用的http庫是libcurl+openssl,使用openssl自帶的證書驗證功能,只能內嵌ca證書,但是我的程式不方便更新,所以最好的方式是使用windows的證書儲存做驗證,這裡有兩種方式。
這種方式的缺點是如果windows沒有安裝服務端使用的ca證書,驗證會失敗。
void
addcertificatesforstore
(x509_store *certstore,
const
char
*subsystemname)
while
(windowscertificate=
certenumcertificatesinstore
(storehandle, windowscertificate))}
}while
(false);if
(storehandle)
}int
sslcontextfunction
(void
* curl,
void
* sslctx,
void
* userdata)
return curle_ok;
}curl_easy_setopt
(curl, curlopt_ssl_verifypeer,1)
;curl_easy_setopt
(curl, curlopt_ssl_ctx_function, sslcontextfunction)
;
這種方式的好處是即使windows證書不全,也能自動更新,缺點是驗證時間可能會很長。
原來windows在驗證證書的時候會預設通過網路獲取ca的crl(證書吊銷列表),檢查該證書是否已被吊銷。我們可以通過libcurl設定不檢查crl(這樣做會不安全)。
curl_easy_setopt
(curl, curlopt_ssl_options, curlsslopt_no_revoke)
;
禁用證書吊銷檢查後依然出錯了,顯示15秒超時。
為了知道是哪一步出問題了,我自己寫了驗證證書**(使用windows介面)來代替libcurl的預設實現,**可以參考libcurl,php,chromium,使用cert_chain_revocation_check_cache_only可以阻止從網路獲取crl。
static
intsslcontextfunction
(void
* curl,
void
* sslctx,
void
* userdata)
static
intsslverifycallback
(x509_store_ctx *x509_store_ctx,
void
*arg)
certctx =
certcreatecertificatecontext
(x509_asn_encoding, derbuf, derlen);if
(certctx ==
null
)/* next fetch the relevant cert chain from the store */
cert_enhkey_usage enhkeyusage =
; cert_usage_match certusage =
; cert_chain_para chainparams =
; lpstr usages=
; enhkeyusage.cusageidentifier =3;
enhkeyusage.rgpszusageidentifier = usages;
certusage.dwtype = usage_match_type_or;
certusage.usage = enhkeyusage;
chainparams.requestedusage = certusage;
dword chainflags = cert_chain_cache_end_cert|cert_chain_revocation_check_chain_exclude_root;if(
!certgetcertificatechain
(null
, certctx,
null
, certctx->hcertstore,
&chainparams, chainflags,
null
,&certchainctx))if
(certchainctx)
/* then verify it against a policy */
auto certname =
x509_get_subject_name
(cert)
;auto index =
x509_name_get_index_by_nid
(certname, nid_commonname,-1
);if(index <0)
asn1_string_to_utf8
(&certnameutf8,
x509_name_entry_get_data
(x509_name_get_entry
(certname, index)))
; std:
:wstring servername;if(
!strutils:
:utf8tounicode
(servername,
(char*)
(certnameutf8)))
ssl_extra_cert_chain_policy_para sslpolicyparams =
; cert_chain_policy_para chainpolicyparams =
; cert_chain_policy_status chainpolicystatus =
; sslpolicyparams.dwauthtype = authtype_server;
sslpolicyparams.pwszservername =const_cast
>
(servername.
c_str()
);sslpolicyparams.fdwchecks =
0x00001000
;// security_flag_ignore_cert_cn_invalid
chainpolicyparams.pvextrapolicypara =
&sslpolicyparams;
chainpolicyparams.dwflags = cert_chain_policy_ignore_all_rev_unknown_flags;
auto verifyresult =
certverifycertificatechainpolicy
(cert_chain_policy_ssl, certchainctx,
&chainpolicyparams,
&chainpolicystatus);if
(verifyresult && chainpolicystatus.dwerror == error_success)
else
else}}
while
(false);if
(derbuf)
if(certnameutf8)
if(certctx)
if(certchainctx)
return ret;
}
呼叫certgetcertificatechain時抓取程式dump,發現程式阻塞在了winhttpgetproxyforurl,原來crypt介面使用winhttp來獲取ctl和根證書,在發起請求前會使用winhttpgetproxyforurl獲取**資訊。
winhttpgetproxyforurl為什麼會一直阻塞呢,使用ida分析發現,winhttpgetproxyforurl內部會使用rpc呼叫wpad服務查詢**資訊,然後呼叫waitformultipleobjects等待返回,所以歸根到底是wpad服務阻塞了。
win7預設開啟了wpad(winhttp web proxy auto-discovery service),該服務可以讓程式自動發現**伺服器,wpad 可以借助 dns 伺服器或 dhcp 伺服器來查詢**自動配置(pac)檔案的位置。關閉掉internet選項-連線-區域網設定-自動檢測設定或者禁用wpad服務後重新執行,發現正常了。
為什麼wpad服務會阻塞呢,呼叫certgetcertificatechain時抓取wpad服務dump,發現wpad會呼叫gethostname獲取本地主機名,然後呼叫getaddrinfo解析,最終阻塞在了nbt_resolvename。
查閱資料發現解析本地主機名超時和netbios以及dhcp有關係,虛擬機器下的windows本地連線會生成乙個「連線特定的dns字尾」localdomain,生成手動填寫本地連線ip位址也正常了。
https證書驗證
目前是在 godaddy 申請的 cn test.com 收到檔案後會列印乙份送行政部法務組,存入保險櫃。檔案構成 nginx 上配置 https 需要兩個引數 新證書驗證 2013年10月13日我們收到了新的 gd bundle.crt 和 test.com.crt 兩個檔案,理論上應該是匹配舊的...
windows下編譯支援https的curl
先編譯好openssl,過程詳見 windows下編譯openssl 編譯好zlib,過程詳見 windows下編譯zlib 官網 2.配置包含檔案目錄和依賴庫目錄 檢視原始碼根目錄下winbuild目錄下build.windows.txt的提示 在源 同級的目錄下建立deps資料夾 此時的目錄結構...
解決windows下gem SSL證書驗證錯誤
執行gem update時證書驗證錯誤 error while executing gem gem remotefetcher fetcherror ssl connect returned 1 errno 0 state sslv3 read server certificate b certif...