不久以前,安全握手是雙方的業務得以實現的一個標記。畢竟,握手是一次面對面的機會,能夠對潛在的合做者進行評價。安全且可信的握手意味着事務的雙方都相信它們正在作的事情對雙方都是有益的。不安全的握手標記着只有一方會對事務有着正確的理解。html
握手的工做方式與在線事務相同。linux
developerWorks 上的前一篇文章「使用 OpenSSL API 進行安全編程,第 1 部分:API 概述」 向您介紹瞭如何使用 OpenSSL 建立基本、簡單的安全鏈接。然而,這篇文章只是展現了基本的默認設置;它並無介紹如何對內容進行定製。不過,我仍然建議你們閱讀這篇文章,這樣可使您對本文的理解更加完整,由於前一篇文章介紹了數字證書的概念,而且介紹瞭如何判斷一個證書是否成功經過了 OpenSSL 的內部驗證。git
本文將深刻介紹 OpenSSL,向您介紹如何加強握手的安全性,防止所謂的 中間人 (MITM)攻擊。算法
關於數字證書數據庫
在本文後面,咱們將介紹如何獲取數字證書並對數字證書進行驗證,所以下面咱們將快速討論一下什麼是數字證書,以及它是如何工做的。若是您熟悉數據加密和 SSL 的知識,就能夠跳過本節。要了解更多有關加密和 SSL 問題的信息,請參閱在本文後面 參考資料中所列出的文章和教程。編程
數字證書的最簡單形式就是 不對稱加密密鑰(asymmetric cryptography key)。目前關於數字證書的標準中都有一些標識信息,在密鑰中也都包含了這些信息。一個典型的數字證書包含全部者的名字(若是這個證書是在一個 Web 服務器上使用的,那麼名字就是完整的域名)以及聯繫信息,還有一個有效日期範圍,以及一個安全性簽名,用來驗證這個證書沒有被篡改。瀏覽器
數字證書可使用 OpenSSL 命令行工具或其餘用於此目的的工具簡單地建立。可是任何人建立的數字證書都有一個信任的問題。數字證書不只僅是一個加密密鑰,它仍是一個在線憑證。證書會向那些試圖與您進行通訊的人證實您的身份。爲了顯示信任關係,數字證書能夠由認證權威(CA)機構進行簽名。安全
認證權威在數字安全性領域充當一個可信的第三方。因爲在在線領域中證實某個實體的身份很是困難,認證權威就接管了這個挑戰。它們爲那些購買證書或對證書進行簽名的用戶的身份提供證實。所以,要信任一個證書,用戶只須要信任證書權威便可。用戶經過擁有並使用 CA 的信任證書來代表本身對認證權威的信任。Verisign 和 Thawte 是很是知名的認證權威。服務器
若是一個證書的安全性曾經受到過威脅,那麼這個證書就會被丟棄 —— 也就是說,將其聲明爲無效。當一個證書被聲明爲無效時,CA 不可能將其通知全部擁有該證書拷貝的人。相反,CA 會發佈一個 證書撤銷列表(Certificate Revocation List)(CRL)。瀏覽器和其餘使用數字證書的程序均可以驗證這個證書已經被其屬主或 CA 撤銷了。網絡
證書的撤銷也可使用 OCSP 協議進行檢查。OCSP 表明 Online Certificate Status Protocol(在線證書狀態協議),它是在 RFC 2560 中定義的。OpenSSL 既有 OCSP 的功能,又有 CRL 的功能,可是對這些功能的介紹已經超出了本文的範圍。目前數字證書所採用的標準是 X.509,這是在 RFC 3280 中定義的。
因爲本文重點要介紹在握手過程當中服務器數字證書的處理,所以讓咱們來深刻介紹一下握手是如何工做的。若是您熟悉 SSL 的過程,能夠跳過本節。
在一個鏈接上開始握手一般是從客戶機向服務器說「Hello」開始的。helllo 消息(在規範中就是這麼說的)包含了客戶機的安全性參數:
服務器會使用本身的 hello 消息進行響應,其中包含了服務器的安全性參數,這些信息與客戶機所提供的信息的類型相同。此時服務器還會發送本身的數字證書。若是客戶機還要爲這個鏈接進行認證,那麼服務器還會發送一個請求,索取客戶機的證書。
當客戶機接收到服務器端的 hello 消息以後,數字證書就要進行驗證了。這包括檢查證書的各類參數,從而確保該證書從未受到過侵害,同時是在它的有效期內使用的。
此處還要執行的另一個步驟是根據鏈接所使用的主機名對證書的名字進行檢查。雖然這不是 SSL 標準的一部分,可是這個步驟倒是高度建議的,它能夠防止中間人攻擊。這個步驟會驗證證書就是來自您認爲它所來自的那個實體。若是這兩個實體在此處不能匹配,那麼就只能懷疑這個證書是無效的。
在客戶機和服務器之間共享的隨機數據用來建立 premaster secret,這是一個只有服務器和客戶機纔會知道的共享祕密值,而且只用於這個會話。這個祕密值會對服務器的數字證書進行加密,併發送給服務器用於驗證客戶機的身份。
若是服務器請求客戶機認證,那麼客戶機就會對這個在握手過程當中隨機生成的數據(只有服務器和客戶機知道它)建立一個單向的 hash 值。客戶機使用本身的私鑰對這個 hash 值進行簽名,並將簽名後的數據和數字證書發送給服務器。服務器使用這些信息對客戶機進行認證。
若是認證成功,那麼服務器和客戶機就會經過一個算法使用這個共享的隨機數據來建立 master secret。從 master secret 中,客戶機和服務器能夠建立 session keys(會話密鑰),這是選擇用來對會話數據進行加密所使用的對稱密碼中的對稱密鑰。
客戶機經過向服務器發送一個代表本身已經完成握手的消息,以及一組加密的單向 hash 值讓服務器進行驗證,從而結束握手的過程。服務器也會向客戶機發送一個相似的消息。客戶機和服務器在結束握手並開始通訊以前,都要對這些數據進行驗證。
雖然這是孩子們玩的一個遊戲,但卻也是在公鑰基礎設施(PKI)上可能發生的一種很嚴重的攻擊。當咱們在討論有關數字證書的問題時,就必須考慮中間人攻擊的問題,由於無論 SSL 鏈接以後的安全參數如何,中間人攻擊均可以讓這些防範措施形同虛設。
假設 Casey 和 Samantha 但願使用 SSL 進行通訊。Isabel 是一個第三方,她截獲了這個鏈接請求,並在他們兩個以前充當代理。當她注意到正在創建一個 SSL 鏈接時,就向 Samantha 假裝成 Casey,向 Casey 假裝成 Samantha。因爲她位於中間,能夠截獲會話雙方的內容。若是這個會話中包含賬號和我的信息,那麼她就可使用這些信息竊取他人的身份了。
在這種狀況中,信任鏈和證書中的通用名能夠防止中間人攻擊的發生。在握手過程當中,會對證書進行交換。在分析證書的有效性時,同時還會檢查可信簽名。若是服務器證書中的通用名是與證書的其他部分一塊兒驗證的,那麼這種攻擊就不攻自破了,對麼?其實不徹底對。
讓咱們假設 Isabel 有一個證書,其中有 Samantha 的名字;而且這個證書由 Casey 信任模型中的一個 CA 進行了簽名。此時只經過檢查通用名並不能避免 MITM 攻擊的發生。證書及其信任關係都是有效的,名字也檢查出來了。咱們如今有了一個大麻煩。
然而,若是考慮一下認證權威,這個問題就並不重要了。大部分認證權威都會在發行帶有我的名的數字證書以前就試圖驗證他的身份就是本人。因爲這個緣由,Isabel 就很難從一個知名的認證權威那裏得到一個帶有 Samantha 名字的數字證書。
若是在 CA 中工做,則能夠消弱這種安全設施的做用。例如,若是 CA 和 Isabel 都在一個公司工做(換而言之,是一個「內部工做」)。那麼用來簽名的密鑰就有可能會被 CA 內部工做的人員竊取,他們隨後就可使用任何人的名字來建立任意的證書。儘管在建立簽名時要使用證書的私有部分,可是採用一些工程的方法或相似的技術也能夠竊取密碼。
MITM 攻擊在使用 代理服務器 的狀況中尤爲重要。由於安全鏈接必須由代理服務器提供一個隧道才能到達目的地,所以惡意的代理服務器就能夠很容易地竊取任何會話。惡意的代理能夠在並不存在隧道時卻假裝成彷彿隧道真正存在同樣。在使用 Internet 上的「匿名代理」時,記得這一點尤爲重要:您正在經過它們的系統來發送用戶名和密碼,怎樣才能相信它們呢?
然而咱們要認識到,這種攻擊並不只僅存在於計算機和數字安全領域。有一個女人曾經使用相似於 MITM 攻擊的技術搶劫了不少家的不少錢(請參閱 參考資料)。
OpenSSL 有一個專門用於數字證書的庫。假設您如今已經有了 OpenSSL 的源代碼,那麼這個庫的源代碼就位於 crypto/x509 和 crypto/x509v3 目錄中。源代碼爲數字證書的處理定義了幾個結構。表 1 中列出了這些結構。
結構 | 功能 |
X509 | 包含全部有關數字證書的數據 |
X509_ALGOR | 提供該證書設計所針對的算法 |
X509_VAL | 該證書有效的時間跨度 |
X509_PUBKEY | 證書的公鑰算法,一般是 RSA 或 DSA |
X509_SIG | 證書的 hash 簽名 |
X509_NAME_ENTRY | 證書所包含的數據的各個項 |
X509_NAME | 包含名字項的堆棧 |
這些只是其中涉及的幾個結構。在 OpenSSL 中使用的大部分 X.509 結構您本身在應用程序中幾乎都不會用到。在上面列出的這些結構中,本文中只使用了兩個:X509 和 X509_NAME。
在這些結構之上,有一些用來處理數字證書的函數。這些函數得名於它們所適用的結構。例如,一個名字以 X509_NAME 開始的函數,一般會應用於一個 X509_NAME 結構。後面咱們會根據須要介紹一些函數。
在數字證書進行信任驗證以前,必須爲在爲安全鏈接設置時建立的 OpenSSL SSL_CTX
對象提供一個默認的信任證書,這可使用幾種方法來提供,可是最簡單的方法是將這個證書保存爲一個 PEM 文件,並使用 SSL_CTX_load_verify_locations(ctx, file, path);
將其加載到 OpenSSL 中。file
是包含一個或多個 PEM 格式的證書的文件的路徑。path
是到一個或多個 PEM 格式文件的路徑,不過文件名必須使用特定的格式。將信任證書保存在一個 PEM 文件中比較簡單,這樣可使 path 參數爲空,以下所示:SSL_CTX_load_verify_locations(ctx, "/path/to/trusted.pem", NULL);
。
儘管當信任證書在一個目錄中有多個單獨的文件時更容易添加或更新,可是您不太可能會如此頻繁地更新信任證書,所以沒必要擔憂這個問題。
在通訊繼續鏈接或接收證書以前,請使用 SSL_get_verify_result()
來肯定 OpenSSL 內部對證書的驗證結果。若是SSL_get_verify_result()
返回的代碼不是 X509_V_OK,那麼這就意味着這個證書無效嗎?這要取決於返回代碼。
一般,若是返回代碼不是 X509_V_OK,那麼這個證書就有問題,或者這個證書存在安全性隱患。時刻要記住一件事情:OpenSSL 在對證書進行驗證時,有一些安全性檢查並無執行,包括證書的失效檢查和對證書中通用名的有效性驗證。
SSL_get_verify_result()
所返回的代碼在 OpenSSL 文檔的 verify
部分中都進行了介紹,這是在 apps 之下列出的。有些代碼的說明是還沒有使用,意味着它們永遠不會返回。有些代碼很是重要,而有些則不過重要。例如,若是因爲沒有加載所保存的信任證書,而不能對信任證書進行驗證,那麼是否繼續進行通訊,就徹底取決於開發者了。
無論驗證結果如何,是否繼續使用一些可能不安全的參數也徹底取決於開發者。因爲證書多是不安全的,所以會返回錯誤代碼。
若是您但願向用戶顯示證書的內容,或者要根據主機名或證書權威對證書進行驗證,那麼就須要檢索證書的內容。要在驗證測試結果以後再檢索證書,請調用 SSL_get_peer_certificate()
。它返回一個指向該證書的 X509 指針,若是證書不存在,就返回 NULL(參見清單 1)。
X509 * peerCertificate; if(SSL_get_verify_result(ssl) == X509_V_OK) peerCertificate = SSL_get_peer_certificate(ssl); else { /* Handle verification error here */ } |
在握手時所提供的服務器的證書應該有一個名字與該服務器的主機名匹配。若是沒有,那麼這個證書就應該標記爲值得懷疑的。內部驗證過程已經對證書進行信任和有效期的驗證;若是這個證書已經超期,或者包含一個不可信的簽名,那麼這個證書就會被標記爲無效的。因爲這不是 SSL 標準的一部分,所以 OpenSSL 並不須要根據主機名對該證書的名字進行檢查。
證書的「名字」其實是證書中的 Common Name 字段。這個字段應該從證書中進行檢索,並根據主機名進行驗證。若是兩者不能匹配,就只有懷疑這個證書無效了。有些公司(例如 Yahoo)就在不一樣的主機上使用相同的證書,即便證書中的 Common Name 只是用於一個主機的。爲了確保這個證書是來自於相同的公司,能夠進行更深刻的檢查,可是這徹底取決於項目的安全性須要。
從證書中檢索通用名須要兩個步驟:
X509_NAME
對象。X509_NAME
對象檢索名字。使用 X509_get_subject_name()
從證書中檢索 X509_NAME
結構。這會返回一個指向 X509_NAME
的對象。從如今開始,請使用X509_NAME_get_text_by_NID()
來檢索通用名,並保存到一個字符串中(如清單 2 所示)。
char commonName [512]; X509_NAME * name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); /* More in-depth checks of the common name can be used if necessary */ if(stricmp(commonName, hostname) != 0) { /* Handle a suspect certificate here */ } |
使用標準的 C 字符串函數或您習慣使用的字符串庫對通用名和主機名進行比較。對不匹配的處理,徹底取決於項目的須要或用戶的決策。若是要更深刻地進行檢查,我建議使用一個單獨的字符串庫來下降複雜性。
在本文中,咱們已經介紹瞭如何加強握手的安全性,從而防止中間人攻擊(攻擊一方假裝成另一個可信源),咱們還介紹了數字證書的概念,以及 OpenSSL API 如何處理數字證書。
記住,在 SSL 會話過程當中加強會話的安全性很是重要,這是由於該鏈接中的全部安全性都是在握手過程當中創建的。遵循本文中概要介紹的每一個步驟到底有多重要取決於項目的要求以及開發者的決定。
Kenneth Ballard 擁有 Peru State College(位於 Peru, Nebraska)的計算機科學專業學士學位,在這裏,他是校報 The Peru State Times 的專職做者。他還擁有 Southwestern Community College(位於 Creston, Iowa)的計算機編程專業副學士學位,在這裏,他是一名半工半讀的 PC 技術員。他的研究領域包括 Java、C++、COBOL、 Visual Basic、網絡、數據庫管理和 Internet 編程。您能夠經過 kenneth.ballard@ptk.org 與 Kenneth 聯繫。