使用curl,libcurl訪問Https
編譯curl,libcurl
下載curl源碼(git clone https://github.com/curl/curl),在目錄curl\winbuild\BUILD.WINDOWS.txt文件中,詳細介紹了使用nmake編譯windows下curl及libcurl庫的相關命令,摘錄如下:
nmake /f Makefile.vc mode=<static or dll> <options> where <options> is one or many of: VC=<6,7,8,9,10,11,12,14> - VC versions WITH_DEVEL=<path> - Paths for the development files (SSL, zlib, etc.) Defaults to sibbling directory deps: ../deps Libraries can be fetched at http://windows.php.net/downloads/php-sdk/deps/ Uncompress them into the deps folder. WITH_SSL=<dll or static> - Enable OpenSSL support, DLL or static WITH_MBEDTLS=<dll or static> - Enable mbedTLS support, DLL or static WITH_CARES=<dll or static> - Enable c-ares support, DLL or static WITH_ZLIB=<dll or static> - Enable zlib support, DLL or static WITH_SSH2=<dll or static> - Enable libSSH2 support, DLL or static ENABLE_SSPI=<yes or no> - Enable SSPI support, defaults to yes ENABLE_IPV6=<yes or no> - Enable IPv6, defaults to yes ENABLE_IDN=<yes or no> - Enable use of Windows IDN APIs, defaults to yes Requires Windows Vista or later, or installation from: https://www.microsoft.com/downloads/details.aspx?FamilyID=AD6158D7-DDBA-416A-9109-07607425A815 ENABLE_WINSSL=<yes or no> - Enable native Windows SSL support, defaults to yes GEN_PDB=<yes or no> - Generate Program Database (debug symbols for release build) DEBUG=<yes or no> - Debug builds MACHINE=<x86 or x64> - Target architecture (default is x86)
由編譯命令可知,編譯curl主要有兩種ssl模式,默認是基於windows的winssl編譯,另一種是基於openssl加密庫。
一、curl+winssl
命令:
nmake /f Makefile.vc mode=dll vc=10
這時默認使用SSPI、IDN、WINSSL等技術,編譯後使用windows系統自帶的CA數字證書文件、ssl加密庫winssl(Schannel and Secure Transport),這種編譯方式有很多優點,一是因為使用windows自帶的加密庫,沒有跨平臺等考慮因素,性能自然是最優的;二是不用引入第三方庫openssl,也不需要顯示設置https CA數字證書文件或者打包根證書到軟件中。但是缺點也是很明顯的,因為windows有很多系統版本,不同版本的ssl有較大區別,早期windows上的ssl安全性能沒那麽高;最嚴重的一個問題是,windows xp等系統在國內用戶量還是很大的,而windows xp不支持SNI技術,如果服務器使用了SNI技術,而且同一個域名配置了多個證書,有可能導致返回證書錯誤,導致https訪問失敗。SNI:Server Name Indication,是為了因對虛擬服務器技術的興起而產生的,就是允許同一臺服務器布置多個域名,在發起https請求的時候,會將請求的域名加到https請求頭中,服務端收到請求後,根據請求頭中的域名返回對應的根證書。
二、curl+openssl
命令:
nmake /f Makefile.vc mode=dll VC=10 WITH_DEVEL=OpenSLL編譯目錄 ENABLE_SSPI=no ENABLE_WINSSL=no
這種編譯方式,首先得下載OpenSSL源碼或者已經編譯好的OpenSSL庫,放到指定目錄並設置到參數WITH_DEVEL參數中,具體的編譯方式可參考http://www.cnblogs.com/openiris/p/3812443.html 。
基於OpenSSL編譯的curl和libcurl,一大優點是使用的較新的SSL加密算法, 安全性較高,而且不需要考慮不同的操作系統SSL庫不同導致的各種問題;缺點就是需要單獨引入OpenSSL庫,需要手動從Mozilla導出根證書,編譯到OpenSSL或者打包到軟件中,在curl中顯示設置加載。 curl官網提供CA數字證書文件下載,地址是https://curl.haxx.se/ca/cacert.pem,更新地址是https://curl.haxx.se/docs/caextract.html 。
遠程更新CA數字證書命令(證書發生改變了才會下載):
curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem
CURL HTTPS參數含義
一、CURL_VERIFY_PEER
該參數含義是驗證HTTPS請求對象的合法性,就是用第三方證書機構頒發的CA數字證書來解密服務端返回的證書,來驗證其合法性。可在編譯時就將CA數字證書編譯進去,也可以通過參數CURLOPT_CAINFO 或者CURLOPT_CAPATH 設置根證書。默認值為1。
二、CURL_VERIFY_HOST
該參數主要用於https請求時返回的證書是否與請求的域名相符合,避免被中間著篡改證書文件。默認值為2。
CURL基於WinSSL和OpenSSL訪問HTTPS示例
一、忽略證書驗證
如果不想驗證PEER和HOST的安全性,可以通過設置
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
二、HTTPS CURL示例
WinSSL:
int Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, std::string &strErrorBuf, const char * pCaPath) { CURLcode res; CURL* curl = curl_easy_init(); char errbuf[CURL_ERROR_SIZE]; if(NULL == curl) { return CURLE_FAILED_INIT; } if(m_bDebug) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug); } curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str()); /* provide a buffer to store errors in */ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str()); curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, TIME_OUT_NUM); curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIME_OUT_NUM); /* set the error buffer as empty before performing a request */ errbuf[0] = 0; res = curl_easy_perform(curl); curl_easy_cleanup(curl); strErrorBuf = errbuf; return res; }
OpenSSL:
int Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, std::string &strErrorBuf, const char * pCaPath) { CURLcode res; CURL* curl = curl_easy_init(); char errbuf[CURL_ERROR_SIZE]; if(NULL == curl) { return CURLE_FAILED_INIT; } if(m_bDebug) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug); } curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str()); /* provide a buffer to store errors in */ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str()); curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); if(pCaPath){ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath); } else{ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem"); } curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, TIME_OUT_NUM); curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIME_OUT_NUM); /* set the error buffer as empty before performing a request */ errbuf[0] = 0; res = curl_easy_perform(curl); curl_easy_cleanup(curl); strErrorBuf = errbuf; return res; }
參考:
a) https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html
b) https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
c) https://curl.haxx.se/docs/sslcerts.html
使用curl,libcurl訪問Https