目錄php
能夠將 SSL 服務器與客戶端之間的通訊配置爲使用單向或雙向 SSL 認證。html
單向 SSL 認證通常是客戶端利用服務器傳過來的信息驗證服務器的合法性,服務器的合法性包括:證書是否過時,發行服務器證書的 CA 是否可靠,發行者證書的公鑰可否正確解開服務器證書的「發行者的數字簽名」,服務器證書上的域名是否和服務器的實際域名相匹配。git
雙向 SSL 認證則除了須要對服務器的合法性進行認證,還須要按照單向 SSL 認證方法對客戶端的合法性進行認證。算法
在金融支付過程當中,對安全要求級別比較高的接口,不只要驗證簽名,還要進行雙向驗證 SSL 證書,所以有些就須要安裝在服務開通以後第三方給咱們發送的安全證書了。shell
爲了便於更好的認識和理解 SSL 協議,這裏着重介紹 SSL 協議的握手協議。SSL 協議既用到了公鑰加密技術又用到了對稱加密技術,對稱加密技術雖然比公鑰加密技術的速度快,但是公鑰加密技術提供了更好的身份認證技術。SSL 的握手協議很是有效的讓客戶和服務器之間完成相互之間的身份認證,其主要過程以下:
① 客戶端的瀏覽器向服務器傳送客戶端 SSL 協議的版本號,加密算法的種類,產生的隨機數,以及其餘服務器和客戶端之間通信所須要的各類信息。
② 服務器向客戶端傳送 SSL 協議的版本號,加密算法的種類,隨機數以及其餘相關信息,同時服務器還將向客戶端傳送本身的證書。
③ 客戶利用服務器傳過來的信息驗證服務器的合法性,服務器的合法性包括:證書是否過時,發行服務器證書的 CA 是否可靠,發行者證書的公鑰可否正確解開服務器證書的「發行者的數字簽名」,服務器證書上的域名是否和服務器的實際域名相匹配。若是合法性驗證沒有經過,通信將斷開;若是合法性驗證經過,將繼續進行第四步。
④ 用戶端隨機產生一個用於後面通信的「對稱密碼」,而後用服務器的公鑰(服務器的公鑰從步驟②中的服務器的證書中得到)對其加密,而後將加密後的「預主密碼」傳給服務器。
⑤ 若是服務器要求客戶的身份認證(在握手過程當中爲可選),用戶能夠創建一個隨機數而後對其進行數據簽名,將這個含有簽名的隨機數和客戶本身的證書以及加密過的「預主密碼」一塊兒傳給服務器。
⑥ 若是服務器要求客戶的身份認證,服務器必須檢驗客戶證書和簽名隨機數的合法性,具體的合法性驗證過程包括:客戶的證書使用日期是否有效,爲客戶提供證書的 CA 是否可靠,發行 CA 的公鑰可否正確解開客戶證書的發行 CA 的數字簽名,檢查客戶的證書是否在證書廢止列表(CRL)中。檢驗若是沒有經過,通信馬上中斷;若是驗證經過,服務器將用本身的私鑰解開加密的「預主密碼」,而後執行一系列步驟來產生主通信密碼(客戶端也將經過一樣的方法產生相同的主通信密碼)。
⑦ 服務器和客戶端用相同的主密碼即「通話密碼」,一個對稱密鑰用於 SSL 協議的安全數據通信的加解密通信。同時在 SSL 通信過程當中還要完成數據通信的完整性,防止數據通信中的任何變化。
⑧ 客戶端向服務器端發出信息,指明後面的數據通信將使用的步驟⑦中的主密碼爲對稱密鑰,同時通知服務器客戶端的握手過程結束。
⑨ 服務器向客戶端發出信息,指明後面的數據通信將使用的步驟⑦中的主密碼爲對稱密鑰,同時通知客戶端服務器端的握手過程結束。
⑩ SSL 的握手部分結束,SSL 安全通道的數據通信開始,客戶和服務器開始使用相同的對稱密鑰進行數據通信,同時進行通信完整性的檢驗。瀏覽器
雙向認證 SSL 協議的具體過程
① 瀏覽器發送一個鏈接請求給安全服務器。
② 服務器將本身的證書,以及同證書相關的信息發送給客戶瀏覽器。
③ 客戶瀏覽器檢查服務器送過來的證書是不是由本身信賴的 CA 中心所簽發的。若是是,就繼續執行協議;若是不是,客戶瀏覽器就給客戶一個警告消息:警告客戶這個證書不是能夠信賴的,詢問客戶是否須要繼續。
④ 接着客戶瀏覽器比較證書裏的消息,例如域名和公鑰,與服務器剛剛發送的相關消息是否一致,若是是一致的,客戶瀏覽器承認這個服務器的合法身份。
⑤ 服務器要求客戶發送客戶本身的證書。收到後,服務器驗證客戶的證書,若是沒有經過驗證,拒絕鏈接;若是經過驗證,服務器得到用戶的公鑰。
⑥ 客戶瀏覽器告訴服務器本身所可以支持的通信對稱密碼方案。
⑦ 服務器從客戶發送過來的密碼方案中,選擇一種加密程度最高的密碼方案,用客戶的公鑰加過密後通知瀏覽器。
⑧ 瀏覽器針對這個密碼方案,選擇一個通話密鑰,接着用服務器的公鑰加過密後發送給服務器。
⑨ 服務器接收到瀏覽器送過來的消息,用本身的私鑰解密,得到通話密鑰。
⑩ 服務器、瀏覽器接下來的通信都是用對稱密碼方案,對稱密鑰是加過密的。
上面所述的是雙向認證 SSL 協議的具體通信過程,這種狀況要求服務器和用戶雙方都有證書。單向認證 SSL 協議不須要客戶擁有 CA 證書,具體的過程相對於上面的步驟,只需將服務器端驗證客戶證書的過程去掉,以及在協商對稱密碼方案,對稱通話密鑰時,服務器發送給客戶的是沒有加過密的(這並不影響 SSL 過程的安全性)密碼方案。 這樣,雙方具體的通信內容,就是加過密的數據,若是有第三方攻擊,得到的只是加密的數據,第三方要得到有用的信息,就須要對加密的數據進行解密,這時候的安全就依賴於密碼方案的安全。而幸運的是,目前所用的密碼方案,只要通信密鑰長度足夠的長,就足夠的安全。這也是咱們強調要求使用 128 位加密通信的緣由。安全
與 SSL 單向認證相關的 curl_easy_setopt
選項有如下幾個:服務器
/CN
值; openssl req -subj "/C=CN/ST=IL/L=ShenZhen/O=Tencent/OU=Tencent/CN=luffichen_server.tencent.com/emailAddress=luffichen@www.tencent.com" ...
與 SSL 雙向認證相關的 curl_easy_setopt
選項有如下幾個:dom
if(!oneway_certification) { // 驗證服務器證書有效性 curl_easy_setopt(m_curl_handler, CURLOPT_SSL_VERIFYPEER, 1); // 檢驗證書中的主機名和你訪問的主機名一致 curl_easy_setopt(m_curl_handler, CURLOPT_SSL_VERIFYHOST, 2); // 指定 CA 證書路徑 curl_easy_setopt(m_curl_handler, CURLOPT_CAINFO, m_ca_cert_file.c_str()); } else { // 不驗證服務器證書 curl_easy_setopt(m_curl_handler, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(m_curl_handler, CURLOPT_SSL_VERIFYHOST, 0); } if(!client_cert_file.empty()) { // 客戶端證書,用於雙向認證 curl_easy_setopt(m_curl_handler, CURLOPT_SSLCERT, client_cert_file.c_str()); } if(!client_cert_type.empty()) { // 客戶端證書類型,用於雙向認證 curl_easy_setopt(m_curl_handler, CURLOPT_SSLCERTTYPE, client_cert_type.c_str()); } if(!private_key.empty()) { // 客戶端證書私鑰,用於雙向認證 curl_easy_setopt(m_curl_handler, CURLOPT_SSLKEY, private_key.c_str()); } if(!private_key_type.empty()) { // 客戶端證書私鑰類型,用於雙向認證 curl_easy_setopt(m_curl_handler, CURLOPT_SSLKEYTYPE, private_key_type.c_str()); } if(!private_key_passwd.empty()) { // 客戶端證書私鑰密碼 curl_easy_setopt(m_curl_handler, CURLOPT_KEYPASSWD, private_key_passwd.c_str()); }
說明:設置 curl 選項時,這裏對空值進行判斷,若是爲空,則不進行雙向認證了。curl
curl -V curl 7.56.0-DEV (Linux) libcurl/7.56.0-DEV OpenSSL/1.0.1e zlib/1.2.3 Release-Date: [unreleased] Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IPv6 Largefile NTLM SSL libz UnixSockets HTTPS-proxy
若是沒有,Features 中沒有 ssl,則須要從新編譯支持 ssl 的 curl 版本(./configure --with-ssl)
在編譯時,可按照 curl 官網給出的方法進行:
./configure --with-ssl If you have OpenSSL installed somewhere else (for example, /opt/OpenSSL) and you have pkg-config installed, set the pkg-config path first, like this: env PKG_CONFIG_PATH=/opt/OpenSSL/lib/pkgconfig ./configure --with-ssl Without pkg-config installed, use this: ./configure --with-ssl=/opt/OpenSSL If you insist on forcing a build without SSL support, even though you may have OpenSSL installed in your system, you can run configure like this: ./configure --without-ssl If you have OpenSSL installed, but with the libraries in one place and the header files somewhere else, you have to set the LDFLAGS and CPPFLAGS environment variables prior to running configure. Something like this should work: CPPFLAGS="-I/path/to/ssl/include" LDFLAGS="-L/path/to/ssl/lib" ./configure If you have shared SSL libs installed in a directory where your run-time linker doesn't find them (which usually causes configure failures), you can provide the -R option to ld on some operating systems to set a hard-coded path to the run-time linker: LDFLAGS=-R/usr/local/ssl/lib ./configure --with-ssl
另外要注意 openssl 不一樣版本對 ssl 協議版本的支持。僅 openssl 1.0.2 及其以上版本目前支持 TLS 1.2 版本的。
在編譯 openssl 1.0.2h 時發現其生成的 libssl 文件爲 libss.so.1.0.0/libcrypto.so.1.0.0 而不是 libssl.so.1.0.2/libcrypto.so.1.0.2,這裏的 ssl 庫的版本和軟件版本的編號是不一致的,這麼作的緣由 Richard Levitte 作了解釋:
We recognised that our shared library version numbering was confusing, so from OpenSSL version 1.1.0 and up, the shared library version retains the two first digits of the OpenSSL version only, which reflects our intent that for any versions x.y.z where x.y stays the same, ABI backward compatibility will be maintained.
當 CURLOPT_SSL_VERIFYPEER
爲 1 時,表示啓用了驗證訪問的服務器合法性,且必須設置 CURLOPT_CAINFO
或 CURLOPT_CAPATH
其中一個,而 CURLOPT_SSL_VERIFYHOST
爲 2 時,表示驗證 CA 證書中的 common name 是否與訪問的服務器域名是否一致。在測試的時候,須要記得爲客戶端側機器添加相應的 host 域名 IP 解析,若是直接使用 IP 訪問也會報 SSL certificate problem, verify that the CA cert is OK
錯誤。
問題的緣由有不少,這裏只列舉一二。
在驗證服務器證書時,找不到CA證書,若是正確設置了 cainfo 或 capath 參數且 CA 證書已是 rootCA,依然出錯,那麼多是證書生成的時候出錯,再從新生成一個;若是 CA 證書由一箇中間證書籤發,rootCA 簽發中間證書,那麼若是服務器沒有提供中間證書,在驗證過程當中,openssl 在造成完整的證書鏈也會報這個錯誤,因此 cat intermediate.crt >> domain.crt
將全部中間證書與rootCA證書捆綁在一塊兒。
curl_easy_setopt - set options for a curl easy handle
curl_easy_setopt
ssl介紹以及雙向認證和單向認證原理
how to install curl and libcurl
OpenSSL 1.0.2h generates libss.so.1.0.0 instead of libssl.so.1.0.2
CURL使用SSL證書訪問HTTPS
HowTo: Create CSR using OpenSSL Without Prompt (Non-Interactive)
curl: (60) SSL certificate : unable to get local issuer certificate