## 第 4 章
### 傳輸層安全( TLS)
SSL( Secure Sockets Layer,安全套接字層)協議最初是網景公司為了保障網上交 易安全而開發的,該協議通過加密來保護客戶個人資料,通過認證和完整性檢查來 確保交易安全。為達到這個目標, SSL 協議在直接位于 TCP 上一層的應用層被實現 (圖 4-1)。 SSL 不會影響上層協議(如 HTTP、電子郵件、即時通訊),但能夠保證 上層協議的網絡通信安全。

圖 4-1:傳輸層安全( TLS)
在正確使用 SSL 的情況下,第三方監聽者只能推斷出連接的端點、加密類型,以及 發送數據的頻率和大致數量,不能實際讀取或修改任何數據。

>*IETF 后來在標準化 SSL 協議時,將其改名為 Transport Layer Security ( TLS,傳輸層安全)。很多人會混用 TLS 和 SSL,但嚴格來講它們并不相 同,因為它們指代的協議版本不同。
SSL 2.0 是該協議第一個公開發布的版本,但由于存在很多安全缺陷很快就被 SSL 3.0 取代。鑒于 SSL 協議是網景公司專有的, IETF 成立了一個小組負責標準化該協 議,后來就有了 RFC 2246,即 TLS 1.0,也就是 SSL 3.0 的升級版。
>本協議與 SSL 3.0 的區別并不特別明顯,但這些區別會嚴重妨礙 TLS 1.0 與 SSL 3.0 之間的互操作性。
——TLS 協議( RFC 2246)
TLS 1.0 自 1999 年 1 月發布后,為解決發現的安全缺陷同時擴展協議的功能, IETF 工作組先后又發布過兩個新版本: 2006 年 4 月發布了 TLS 1.1, 2008 年 8 月發布了 TLS 1.2。從內部來看, SSL 3.0 實現與后續所有 TLS 版本很相似,很多客戶端到今 天還在支持 SSL 3.0 和 TLS 1.0。當然,要保護用戶免受攻擊,最好還是升級到最新 版本。
>*TLS 設計的初衷是在可靠的傳輸協議(如 TCP)之上運行。可是,有實現 把它放到了數據報協議(如 UDP)之上。 RFC 6347,即 DTLS( Datagram Transport Layer Security,數據報傳輸層安全)就旨在以 TLS 協議為基礎, 同時兼顧數據報交付模式并提供類似的安全保障。*
## 4.1 加密、 身份驗證與完整性
TLS 協議的目標是為在它之上運行的應用提供三個基本服務:加密、身份驗證和數 據完整性。從技術角度講,并不是所有情況下都要同時使用這三個服務。比如,可 以接受證書但不驗證其真實性,而前提是你非常清楚這樣做有什么安全風險且有防 范措施。實踐中,安全的 Web 應用都會利用這三個服務。
- 加密
混淆數據的機制
- 身份驗證
驗證身份標識有效性的機制
- 完整性
檢測消息是否被篡改或偽造的機制
為了建立加密的安全數據通道,連接雙方必須就加密數據的密鑰套件和密鑰協商一 致 。 TLS 協議規定了一套嚴密的握手程序用于交換這些信息,相關內容將在 4.2 節 “ TLS 握手”中介紹。握手機制中設計最巧妙的地方,就是其使用的公鑰密碼系統 (也稱“非對稱密鑰加密”),這套系統可以讓通信雙方不必事先“認識”即可商定共 享的安全密鑰,而且協商過程還是通過非加密通道完成的。
握手過程中, TLS 協議還允許通信兩端互相驗明正身。在瀏覽器中,驗證機制允許 客戶端驗證服務器就是它想聯系的那個(比如,銀行),而不是通過名字或 IP 地址偽 裝的目標。這個驗證首先需要建立“認證機構信任鏈”( Chain of Trust and Certificate Authorities)。此外,服務器也可以選擇驗證客戶端的身份。比如,公司的代理服務器 可以驗證所有雇員,每位雇員都應該有公司簽發的獨一無二的認證證書。
除了密鑰協商和身份驗證, TLS 協議還提供了自己的消息分幀機制,使用 MAC ( Message Authentication Code,消息認證碼)簽署每一條消息。 MAC 算法是一個單 向加密的散列函數(本質上是一個校驗和),密鑰由連接雙方協商確定。只要發送 TLS 記錄,就會生成一個 MAC 值并附加到該消息中。接收端通過計算和驗證這個 MAC 值來判斷消息的完整性和可靠性。
上述三種機制為 Web 通信構建了一個安全的環境。所有現代 Web 瀏覽器都支持多 種加密套件,能夠驗證客戶端和服務器,并能對每條記錄進行消息完整性檢查。
>**Web 代理、 中間設備、 TLS 與新協議**
>HTTP 良好的擴展能力和獲得的巨大成功,使得 Web 上出現了大量代理和中間設 備:緩存服務器、安全網關、 Web 加速器、內容過濾器,等等。有時候,我們知 道這些設備的存在(顯式代理),而有時候,這些設備對終端用戶則完全不可見。 然而,這些服務器的存在及成功也給那些試圖脫離 HTTP 協議的人帶了一些不便。 比如,有的代理服務器只會簡單地轉發自己無法解釋的 HTTP 擴展或其他在線格 式( wire format),而有的則不管是否必要都會對所有數據執行自己設定的邏輯, 還有一些安全設備可能會把本來正常的數據誤判成惡意通信。
換句話說,現實當中如果想脫離 HTTP 和 80 端口的語義行事,經常會遭遇各種部 署上的麻煩。比如,某些客戶端表現正常,另一些可能就會異常,甚至在某個網 段表現正常的客戶端到了另一個網段又會變得異常。
為解決這些問題,出現了一些新協議和對 HTTP 的擴展,比如 WebSocket、 SPDY 等。 這些新協議一般要依賴于建立 HTTPS 信道,以繞過中間代理,從而實現可靠 的部署,因為加密的傳輸信道會對所有中間設備都混淆數據。這樣雖然解決了中 間設備的問題,但卻導致通信兩端不能再利用這些中間設備,從而與這些設備提 供的身份驗證、緩存、安全掃描等功能失之交臂。
你或許一直想不通為什么大多數 WebSocket 手冊都告訴你要使用 HTTPS 向移動客 戶端發送數據,現在你應該明白了。隨著時間推移,網絡上的中間設備通過升級 也開始能識別新協議,基于 HTTPS 部署的要求也將逐漸弱化。到時候,除非你的 會話真的需要 TLS 提供的加密、身份驗證和完整性檢查功能,否則完全可以不用 HTTPS !
## 4.2 TLS握手
客戶端與服務器在通過 TLS 交換數據之前,必須協商建立加密信道。協商內容包括 TLS 版本、加密套件,必要時還會驗證證書。然而,協商過程的每一步都需要一個 分組在客戶端和服務器之間往返一次(圖 4-2),因而所有 TLS 連接啟動時都要經歷 一定的延遲。

圖 4-2: TLS 握手協議
>*圖 4-2 假設“光通過光纖”的單程時間都是 28 ms,也就是前面 TCP 連接 中從紐約到倫敦之間的時間,另見表 1-1。
>- 0 ms: TLS 在可靠的傳輸層( TCP)之上運行,這意味著首先必須完成 TCP 的“三 次握手”,即一次完整的往返。
>- 56 ms: TCP 連接建立之后,客戶端再以純文本形式發送一些規格說明,比如它所運 行的 TLS 協議的版本、它所支持的加密套件列表,以及它支持或希望使用的另外一 些 TLS 選項。
>- 84 ms:然后,服務器取得 TLS 協議版本以備將來通信使用,從客戶端提供的加密 套件列表中選擇一個,再附上自己的證書,將響應發送回客戶端。作為可選項,服 務器也可以發送一個請求,要求客戶端提供證書以及其他 TLS 擴展參數。
>-112 ms:假設兩端經過協商確定了共同的版本和加密套件,客戶端也高高興興地 把自己的證書提供給了服務器。然后,客戶端會生成一個新的對稱密鑰,用服務 器的公鑰來加密,加密后發送給服務器,告訴服務器可以開始加密通信了。到 目前為止,除了用服務器公鑰加密的新對稱密鑰之外,所有數據都以明文形式 發送。
>-140 ms:最后,服務器解密出客戶端發來的對稱密鑰,通過驗證消息的 MAC 檢 測消息完整性,再返回給客戶端一個加密的“ Finished”消息。
>-168 ms:客戶端用它之前生成的對稱密鑰解密這條消息,驗證 MAC,如果一切 順利,則建立信道并開始發送應用數據。*
>*新 TLS 連接要完成一次“完整的握手”需要兩次網絡往返。另外,還可以使 用“簡短握手”,只需一次往返,詳細信息請參見 4.3 節“ TLS 會話恢復”。 *
協商建立 TLS 安全信道是一個復雜的過程,很容易出錯。好在服務器和瀏覽器會替 我們做好這些工作,我們要做的就是提供和配置證書!
總之,盡管我們的 Web 應用不一定參與上述過程,但最重要的是知道每一個 TLS 連接在 TCP 握手基礎上最多還需要兩次額外的往返。這些都會增加實際交換數據之 前的等待時間!如果考慮不周,通過 TLS 交付數據很可能會引入幾百甚至幾千 ms 的網絡延遲。
>**公鑰與對稱密鑰加密的性能**
>公鑰加密系統( http://en.wikipedia.org/wiki/Public-key_cryptography)只在建立 TLS 信道的會話中使用。在此期間,服務器向客戶端提供它的公鑰,客戶端生成對稱 密鑰并使用服務器的公鑰對其加密,然后再將加密的對稱密鑰返回服務器。服務 器繼而用自己的私鑰解密出客戶端發來的對稱密鑰。
接下來,客戶端與服務器間的通信就全都使用客戶端生成的共享密鑰加密,這就 是對稱密鑰加密。之所以這樣設計,很大程度上是出于性能考慮,因為公鑰加密 需要很大的計算量。為了演示兩者的差別,假如你的電腦上安裝了 OpenSSL,可 以試試以下兩條命令:
? $> openssl speed rsa
? $> openssl speed aes
需要注意,這兩條命令涉及的單位不能直接相比: RSA 測試會提供一個摘要表格, 列出不同密鑰大小每秒的操作數,而 AES 性能通過每秒的字節數來度量。無論如 何,都不難看出 RSA 操作(完整的 TLS 握手)的數量,在推薦的 1024 或 2048 位的密鑰長度下都很可能成為瓶頸。
實際的數值可能會因硬件、核心數量、 TLS 版本、服務器配置、典型的服務負載, 以及其他因素不同而有很大差異。請大家不要輕信一些廣告宣傳或過時的測試報 告,最好還是在自己的硬件上運行上述測試。
### 4.2.1 應用層協議協商( ALPN)
理論上,網絡上的任意兩端都可以使用自定義的協議進行通信。為此,需要提前確 定使用什么協議、指定端口號( HTTP 是 80, TLS 是 443),并配置所有客戶端和服 務器使用它們。然而在實踐中,這種方法效率很低且很難做到。因為每個端口都必 須得到認可,而防火墻及其他中間設備通常只允許在 80 和 443 端口上通信。
于是,為了簡化自定義協議的部署,我們往往必須重用 80 或 443 端口,再通過額 外的機制協商確定協議。 80 端口是為 HTTP 保留的,而 HTTP 規范還專門為協商協 議規定了一個 Upgrade 首部。可是,使用 Upgrade 需要一次額外的往返時間,且由 于很多中間設備的存在,協商結果也不可靠。要了解更多信息,請參考 4.1 節中的 “ Web 代理、中間設備、 TLS 與新協議”。
>*12.3.9 節“有效的 HTTP 2.0 升級與發現”中有一個使用 HTTP Upgrade 的 實際例子。*
解決辦法不難想象,那就是使用 443 端口,這是給(運行于 TLS 之上的)安全 HTTPS 會話保留的。由于端到端的加密信道對中間設備模糊了數據,因此這種方式 就成為了一種部署任意新應用協議的可靠而快捷的方式。不過,雖然使用 TLS 保障 了可靠性,但我們還需要一種機制來協商協議。
作為 HTTPS 會話,當然可以利用 HTTP 的 Upgrade 機制來協商,但這會導致一次額 外的往返,造成延遲。那在 TLS 握手的同時協商確定協議可行嗎?
顧名思義,應用層協議協商( ALPN, Application Layer Protocol Negotiation)作為 TLS 擴展,讓我們能在 TLS 握手的同時協商應用協議(圖 4-2),從而省掉了 HTTP 的 Upgrade 機制所需的額外往返時間。具體來說,整個過程分如下幾步:
- 客戶端在 ClientHello 消息中追加一個新的 ProtocolNameList 字段,包含自己支 持的應用協議;
- 服務器檢查 ProtocolNameList 字段,并在 ServerHello 消息中以 ProtocolName 字 段返回選中的協議。
服務器可以從中選擇一個協議名,否則如果不支持其中的任何協議,則斷開連接。 只要 TLS 握手完成、建立了加密信道并就應用協議達成一致,客戶端與服務器就可 以立即通信。
>*ALPN 拋開了 HTTP 的 Upgrade 機制,省卻了一次往返的延遲。不過, TLS 握手還是必需的。事實上, ALPN 協商不會比基于非加密信道的 HTTP Upgrade 更快,它只能保證通過 TLS 進行協議協商不會更慢。*
>**NPN 與 ALPN 的淵源**
>NPN( Next Protocol Negotiation,下一代協議協商)是谷歌在 SPDY 協議中開發的 一個 TLS 擴展,目的是通過在 TLS 握手期間協商應用協議來提高效率。聽著耳熟 嗎?最終結果與 ALPN 功能等價。
ALPN 是 IETF 在 NPN 基礎上修訂并批準的版本。在 NPN 中,服務器廣播自己 支持的協議,客戶端選擇和確認協議。而在 ALPN 中,交換次序顛倒過來了,客 戶端先聲明自己支持的協議,服務器選擇并確認協議。這樣修改的目的是為了讓 ALPN 與其他協議協商標準保持一致。
換句話說, ALPN 是 NPN 的繼任者,而 NPN 已經廢棄。原來配置為使用 NPN 的 客戶端和服務器,都需要重新配置升級到 ALPN。
### 4.2.2 服務器名稱指示( SNI)
網絡上可以建立 TCP 連接的任意兩端都可以建立加密 TLS 信道,客戶端只需知道 另一端的 IP 地址即可建立連接并進行 TLS 握手。然而,如果服務器想在一個 IP 地 址為多個站點提供服務,而每個站點都擁有自己的 TLS 證書,那怎么辦?這問題看 似有點怪,但其實一點都不怪。
為了解決這個問題, SNI( Server Name Indication,服務器名稱指示)擴展被引入 TLS 協議,該擴展允許客戶端在握手之初就指明要連接的主機名。 Web 服務器可以檢查 SNI 主機名,選擇適當的證書,繼續完成握手。
>**TLS、 HTTP 及專用 IP**
>TLS+SNI 機制與 HTTP 中發送 Host 首部是相同的,只不過后者是客戶端要在請求 中包含站點的主機名。總之,都是相同的 IP 地址服務于不同的域名,而區分不同 域名的手段就是 SNI 或 Host。
遺憾的是,很多舊版本的客戶端( Windows XP 上的大多數 IE、 Android 2.2 等平 臺上的瀏覽器)都不支持 SNI。如果你想與這些舊版本的客戶端進行 TLS 通信, 就得為每個主機準備一個專用 IP 地址。
## 4.3 TLS會話恢復
完整 TLS 握手會帶來額外的延遲和計算量,從而給所有依賴安全通信的應用造成嚴 重的性能損失。為了挽回某些損失, TLS 提供了恢復功能,即在多個連接間共享協 商后的安全密鑰。
### 4.3.1 會話標識符
最早的“會話標識符”( Session Identifier, RFC 5246)機制是在 SSL 2.0 中引入的, 支持服務器創建 32 字節的會話標識符,并在上一節我們討論的完整的 TLS 協商期 間作為其“ ServerHello”消息的一部分發送。
在內部,服務器會為每個客戶端保存一個會話 ID 和協商后的會話參數。相應地,客 戶端也可以保存會話 ID 信息,并將該 ID 包含在后續會話的“ ClientHello”消息中, 從而告訴服務器自己還記著上次握手協商后的加密套件和密鑰呢,這些都可以重用。 假設客戶端和服務器都可以在自己的緩存中找到共享的會話 ID 參數,那么就可以進 行簡短握手(圖 4-3)。否則,就要重新啟動一次全新的會話協商,生成新的會話 ID。
借助會話標識符可以節省一次往返,還可以省掉用于協商共享加密密鑰的公鑰加密 計算。由于重用了之前協商過的會話數據,就可以迅速建立一個加密連接,而且同 樣安全。
>*實際應用中,大多數 Web 應用會嘗試與同一個主機建立多個連接,以便并 行取得資源。在這種情況下,會話恢復就成為減少延遲及兩端計算量的必 備優化手段。
大多數現代瀏覽器在打開到相同服務器的新連接之前,都會有意等待第一 個 TLS 連接完成。這樣,后續的 TLS 連接就可以重用第一個 SSL 會話, 從而避免重新握手造成的損失。*

圖 4-3:簡短 TLS 握手協議
由于服務器必須為每個客戶端都創建和維護一段會話緩存,“會話標識符”機制在實 踐中也會遇到問題,特別是對那些每天都要“接待”幾萬甚至幾百萬獨立連接的服 務器來說,問題就更多了。由于每個打開的 TLS 連接都要占用內存,因此需要一套 會話 ID 緩存和清除策略,對于擁有很多服務器而且為獲得最佳性能必須使用共享 TLS 會話緩存的熱門站點而言,部署這些策略絕非易事。
當然,這些問題并非無解,今天的很多高流量站點都在成功地使用會話標識符。只 不過對于多服務器部署來說,為確保會話緩存的良性循環,必須對會話標識符進行 周密的規劃和系統的設計。
### 4.3.2 會話記錄單
為了解決上述服務器端部署 TLS 會話緩存的問題,“會話記錄單”( Session Ticket, RFC 5077)機制出臺了,該機制不用服務器保存每個客戶端的會話狀態。相反,如 果客戶端表明其支持會話記錄單,則服務器可以在完整 TLS 握手的最后一次交換中 添加一條“新會話記錄單”( New Session Ticket)記錄,包含只有服務器知道的安 全密鑰加密過的所有會話數據。
然后,客戶端將這個會話記錄單保存起來,在后續會話的 ClientHello 消息中,可以將其包含在 SessionTicket 擴展中。這樣,所有會話數據只保存在客戶端,而由于 數據被加密過,且密鑰只有服務器知道,因此仍然是安全的。
我們這里所說的會話標識符和會話記錄單機制,也經常被人稱為“會話緩存”或“無 狀態恢復”機制。無狀態恢復機制的優點主要是消除了服務器端的緩存負擔,通過要 求客戶端在與服務器建立新連接時提供會話記錄單簡化了部署(除非記錄單過期)。
>*實踐中,在一組負載均衡服務器上部署會話記錄單仍然要求周密規劃和系 統設計。比如,所有服務器一開始都擁有相同的會話密鑰,然后再通過另 外的機制定期在所有服務器端輪換共享的密鑰。*
## 4.4 信任鏈與證書頒發機構
身份驗證是建立每個 TLS 連接必不可少的部分。畢竟,加密信道兩端可以是任何機 器,包括攻擊者的機器。為此,必須確保我們與之交談的計算機是可信任的,否則 之前的工作都是徒勞。為理解如何驗證通信兩端的身份,下面我們以張三和李四之 間的驗證為例簡單說明一下:
- 張三和李四分別生成自己的公鑰和私鑰;
- 張三和李四分別隱藏自己的私鑰;
- 張三向李四公開自己的公鑰,李四也向張三公開自己的公鑰;
- 張三向李四發送一條新消息,并用自己的私鑰簽名;
- 李四使用張三的公鑰驗證收到的消息簽名。
信任是上述交流的關鍵。公鑰加密可以讓我們使用發送端的公鑰驗證消息是否使用 了正確的私鑰簽名,但認可發送端仍然是基于信任。在上述交流中,張三和李四可 以當面交換自己的公鑰,因為他們互相認識,能夠保證不被別人冒名頂替。可以說, 他們已經通過之前安全(物理)的握手確認了對方。
接下來,張三收到王五發來的一條消息。張三從未見過王五,但王五自稱是李四的 朋友。事實上,為了證明自己是李四的朋友,王五還請李四用李四的私鑰簽署了自 己的公鑰,并在消息中附上了簽名(圖 4-4)。此時,張三首先檢查王五公鑰中李四的簽名。他知道李四的公鑰,因而可以驗證李四確實簽署了王五的公鑰。由于他信 任李四對王五的簽名,所以就接受了王五的消息,并對消息進行完整性檢查,以確 保消息確實來自王五。
剛才這個過程建立了一個信任鏈:張三信任李四,李四信任王五, 通過信任的傳遞,張 三信任王五。只要這條鏈上的人不會被冒名頂替,我們就可以繼續擴展這個信任網絡。

圖 4-4:張三、李四和王五的信任鏈
Web 以及瀏覽器中的身份驗證與上述過程相同,這就意味著此時此刻你應該問自 己:我的瀏覽器信任誰?我在使用瀏覽器的時候信任誰?這個問題至少有三個答案。
- 手工指定證書
所有瀏覽器和操作系統都提供了一種手工導入信任證書的機制。至于如何獲得證 書和驗證完整性則完全由你自己來定。
- 證書頒發機構
CA( Certificate Authority,證書頒發機構)是被證書接受者(擁有者)和依賴證 書的一方共同信任的第三方。
- 瀏覽器和操作系統
每個操作系統和大多數瀏覽器都會內置一個知名證書頒發機構的名單。因此,你 也會信任操作系統及瀏覽器提供商提供和維護的可信任機構。
實踐中,保存并手工驗證每個網站的密鑰是不可行的(當然,如果你愿意,也可 以)。現實中最常見的方案就是讓證書頒發機構替我們做這件事(圖 4-5):瀏覽器 指定可信任的證書頒發機構(根 CA),然后驗證他們簽署的每個站點的責任就轉移 到了他們頭上,他們會審計和驗證這些站點的證書沒有被濫用或冒充。持有 CA 證 書的站點的安全性如果遭到破壞,那撤銷該證書也是證書頒發機構的責任。

圖 4-5:證書頒發機構簽署數字證書
所有瀏覽器都允許用戶檢視自己安全連接的信任鏈,常見的訪問入口就是地址欄頭 兒上的鎖圖標,點擊即可查看(圖 4-6)。
- igvita.com 證書由 StartCom Class 1 Primary Intermediate Server 簽發。 ? StartCom Class 1 Primary Intermediate Server 證書由 StartCom Certification Authority 簽發。
- StartCom Certification Authority 被認為是根證書頒發機構。

圖 4-6: igvita.com 的證書信任鏈(谷歌 Chrome v25)
整個鏈條的“信任依據”是根證書頒發機構,在這里就是 StartCom Certification Authority。每個瀏覽器都會內置一個可信任的證書頒發機構(根機構)的名單,在 此瀏覽器相信而且能夠驗證 StartCom 根證書。實際上,通過瀏覽器到瀏覽器開發商,再到 StartCom 證書頒發機構的信任鏈傳遞,可以把信任擴展至目標站點。
>*所有操作系統和瀏覽器在默認情況下都會提供一個它們信任的證書頒發機 構名單。如果你想進一步了解,可以搜索并研究研究這個名單。
實踐中,知名和可信任的證書頒發機構有好幾百個,而這也是系統經常遭 到責難的原因。想象一下,這么多證書頒發機構無疑會構成一個較大的攻 擊面,從而給壞人潛入你的瀏覽器信任鏈提供可乘之機。*
## 4.5 證書撤銷
有時候,出于種種原因,證書頒發者需要撤銷或作廢證書,比如證書的私鑰不再安 傳輸層安全全、證書頒發機構本身被冒名頂替,或者其他各種正常的原因,像以舊換新或所屬 關系更替等。為此,證書本身會包含如何檢測其是否過期的指令(圖 4-7)。為確保 信任鏈不被破壞,通信的任何一端都可以根據嵌入的指令和簽名檢查鏈條中每個證 書的狀態。

圖 4-7: igvita.com 證書中的 CRL 和 OCSP 指令(谷歌 Chrome v25)
### 4.5.1 證書撤銷名單( CRL)
CRL( Certificate Revocation List,證書撤銷名單)是 RFC 5280 規定的一種檢查所 有證書狀態的簡單機制:每個證書頒發機構維護并定期發布已撤銷證書的序列號名 單。這樣,任何想驗證證書的人都可以下載撤銷名單,檢查相應證書是否榜上有名。 如果有,說明證書已經被撤銷了。
CRL 文件本身可以定期發布、每次更新時發布,或通過 HTTP 或其他文件傳輸協議 來提供訪問。這個名單同樣由證書頒發機構簽名,通常允許被緩存一定時間。實踐 中,這種機制效果很好,但也存在一些問題:
- CRL 名單會隨著要撤銷的證書增多而變長,每個客戶端都必須取得包含所有序列 號的完整名單;
- 沒有辦法立即更新剛剛被撤銷的證書序列號,比如客戶端先緩存了 CRL,之后某 證書被撤銷,那到緩存過期之前,該證書將一直被視為有效。
### 4.5.2 在線證書狀態協議( OCSP)
為解決 CRL 機制的上述問題, RFC 2560 定義了 OCSP( Online Certificate Status Protocol,在線證書狀態協議),提供了一種實時檢查證書狀態的機制。與 CRL 包含 被撤銷證書的序列號不同, OCSP 支持驗證端直接查詢證書數據庫中的序列號,從 而驗證證書鏈是否有效。總之, OCSP 占用帶寬更少,支持實時驗證。
然而,沒有什么機制是完美無缺的!實時 OCSP 查詢也帶了一些問題:
-證書頒發機構必須處理實時查詢;
- 證書頒發機構必須確保隨時隨地可以訪問;
- 客戶端在進一步協商之前阻塞 OCSP 請求;
- 由于證書頒發機構知道客戶端要訪問哪個站點,因此實時 OCSP 請求可能會泄露 客戶端的隱私。
>*實踐中, CRL 和 OCSP 機制是互補存在的,大多數證書既提供指令也支持 查詢。 更重要的倒是客戶端的支持和行為。有的瀏覽器會分發自己的 CRL 名單, 有的瀏覽器從證書頒發機構取得并緩存 CRL 文件。類似地,有的瀏覽器會 進行實時 OCSP 檢查,但在 OCSP 請求失敗的情況下行為又會有所不同。要 了解具體的情況,可以檢查瀏覽器和操作系統的證書撤銷網絡設置。 *
## 4.6 TLS記錄協議
與位于其下的 IP 或 TCP 層沒有什么不同, TLS 會話中交換的所有數據同樣使用規 格明確的協議進行分幀(圖 4-8)。 TLS 記錄協議負責識別不同的消息類型(握手、 警告或數據,通過“內容類型”字段),以及每條消息的安全和完整性驗證。

圖 4-8: TLS 記錄結構
交付應用數據的典型流程如下。
- 記錄協議接收應用數據。
- 接收到的數據被切分為塊:最大為每條記錄 214 字節,即 16 KB。
- 壓縮應用數據(可選)。
- 添加 MAC( Message Authentication Code)或 HMAC。
- 使用商定的加密套件加密數據。
以上幾步完成后,加密數據就會被交給 TCP 層傳輸。接收端的流程相同,順序相 反:使用商定的加密套件解密數據、驗證 MAC、提取并把數據轉交給上層的應用。
同樣,值得慶幸的是以上過程都由 TLS 層幫我們處理,而且對大多數應用都是完全 透明的。不過,記錄協議也帶來了一些重要的限制,務必要注意:
- TLS 記錄最大為 16 KB;
- 每條記錄包含 5 字節的首部、MAC(在 SSL 3.0、TLS 1.0、TLS 1.1 中最多 20 字節, 在 TLS 1.2 中最多 32 字節),如果使用塊加密則還有填充;
- 必須接收到整條記錄才能開始解密和驗證。
有可能的話,應該自主選擇記錄大小,這也是一項重要的優化。小記錄會因記錄分 幀而招致較大開銷,大記錄在被 TLS 層處理并交付應用之前,必須通過 TCP 傳輸 和重新組裝。
## 4.7 針對TLS的優化建議
鑒于網絡協議的分層架構,在 TLS 之上運行應用與直接通過 TCP 通信沒有什么不 同。正因為如此,只需要對應用進行很少改動甚至不用改動就可以讓它基于 TLS 通 信。當然,前提是你根據 2.5 節“針對 TCP 的優化建議”中的最佳實踐去做了。
不過,你還應該關心 TLS 部署運維的一些方法,比如服務器的部署方式和地理位 置、 TLS 記錄及內存緩沖區大小、是否支持簡短握手,等等。關注這些細節不僅能 大大改善用戶體驗,還能幫你節省運維成本。
### 4.7.1 計算成本
建立和維護加密信道給兩端帶來了額外的計算復雜度。特別地,首先有一個非對稱 (公鑰)加密,我們在 4.2 節“ TLS 握手”中介紹過。然后,在握手期間確定共享密 鑰,作為對后續 TLS 記錄加密的對稱密鑰。
如前所述,公鑰加密與對稱加密相比,需要更大的計算工作量。因此,在 Web 發展 早期,通常都需要專門的硬件來進行“ SSL 卸載”。好在現在不這樣了。現代硬件突飛猛進的發展為減小這種損失提供了強力支持,原先需要專門硬件來做的工作,今 天直接通過 CPU 就能完成。Facebook、谷歌等大公司都通過 TLS 向數百萬用戶提 供服務,相關的計算工作通過軟件和普通的計算機就能完成。
>今年( 2010 年) 1 月份, Gmail 切換為默認使用 HTTPS。在此之前,它只是 一個選項,而現在所有用戶都在使用 HTTPS 在瀏覽器與谷歌之間隨時隨地 安全地收發郵件。為做到這一點,我們并沒有部署額外的機器,也沒有專用 硬件。在我們的前端機器上, SSL/TLS 計算只占 CPU 負載的不到 1%,每個 連接只占不到 10 KB 的內存,以及不到 2% 的網絡資源。很多人認為 SSL/ TLS 占用了很多 CPU 時間, 我們希望上述幾個數字(首次公開)能消除人 們的顧慮。 如果你現在不想往下再看了, 那只需要記住一件事: SSL/TLS 計算已經不是 問題了。
——Adam Langley(谷歌)
我們已經大規模部署了 TLS, 既使用了硬件也使用軟件負載均衡器。我們發 現當前基于軟件的 TLS 實現在普通 CPU 上已經運行得足夠快,無需借助專 門的加密硬件就能夠處理大量的 HTTPS 請求。 我們使用運行于普通硬件上 的軟件提供所有 HTTPS 服務。
——Doug Beaver( Facebook)
盡管如此,像 4.3 節“ TLS 會話恢復”中介紹的技巧對優化性能依舊很重要,它能 幫你減少計算損失和 TLS 握手期間的公鑰加密延遲。 CPU 也不應該處理本不該處理 的計算。
>*說到優化 CPU 周期,一定別忘了把你的 SSL 庫升級到最新版本,在此基 礎上再構建 Web 服務器和代理服務器!最新版的 OpenSSL 在性能方面有 了明顯的提升,而你系統中默認的 OpenSSL 庫很有可能已經過時了。
### 4.7.2 盡早完成( 握手)
建立連接的延遲體現在每個 TLS 連接上,包括新連接和恢復的連接,因此是優化的 重點。我們知道 TCP 連接首先要經過 2.1 節描述的“三次握手”,兩端要通過一次 完整的往返交換 SYN/SYN-ACK 分組。其次, 4.2 節介紹的“ TLS 握手”在完整的 情況下,需要兩次額外的往返,或在 4.3 節描述的“ TLS 會話恢復”的情況下,需 要一次額外的往返。
最差的情況,在實際交換應用數據之前,建立 TCP 和 TLS 連接的過程要經過三次往 返!以前面紐約的客戶端連接倫敦的服務器為例,每次往返耗時 56 ms(見表 1-1), 那么建立完整的 TCP 和 TLS 連接需要三次往返即 168 ms,而恢復 TLS 會話需要 112 ms。當然, 56 ms 是最樂觀的數字,正常情況下延遲越長,性能損失越嚴重。
因為所有 TLS 會話都是在 TCP 之上完成的,因此 2.5 節“針對 TCP 的優化建議” 在這里也完全適用。如果說重用 TCP 連接對于非加密通信是一個重要的優化手段, 那么這個手段對運行在 TLS 上的應用同樣至關重要。換句話說,只要能省掉握手, 就應該省掉。如果必須握手,那么還有一個可能的技巧:盡早完成。
第 1 章討論過,我們不能指望延遲在將來能下降多少,因為光電信號的傳輸速度已 經是光速的一個非常小的常數因子了。不能讓分組傳播更快,但可以縮短傳播距 離!盡早完成就是這么一個技巧,即通過把服務器放到離用戶更近的地方(圖 4-9), 讓客戶端與服務器之間往返延遲最少。

圖 4-9:客戶端連接的盡早完成
做到盡早完成的最簡單方式,就是在世界各地的服務器上緩存或重復部署數據和服 務,而不要讓所有用戶都通過跨海或跨大陸光纜連接到一個中心原始服務器。當然, 這正是 CDN( Content Delivery Networks,內容分發網絡)服務的內容:通過使用 本地代理服務器分流負載等手段降低延遲。
雖然 CDN 最常用于在全球優化分發靜態資源,但其優點并不止于此。距離客戶端 更近的服務器還可以縮短 TLS 會話,因為 TCP 和 TLS 握手的對象都是近處的服務 器,所以建立連接的總延遲就會顯著減少。相應地,本地代理服務器則可以與原始 服務器建立一批長期的安全連接,全權代理請求與響應。
簡言之,把服務器放到接近客戶端的地方能夠節約 TCP 和 TLS 握手的時間!大多數 CDN 提供商都提供這種服務,當然如果你愿意也可以以最小的成本部署自己的基礎設施:在全球幾大數據中心租用一些云服務器,然后在每臺服務器上都運行代理(服 務器)程序,把請求轉發到你的原始服務器,再加上地理 DNS 負載均衡系統即可。
>**不緩存的原始獲取 **
>使用 CDN 或代理服務器取得資源的技術,如果要根據用戶定制或者涉及隱私 數據,則不能做到全球緩存,這種情況被稱為“不緩存的原始獲取”( uncached origin fetch)。
雖然只有把數據緩存到全球各地的服務器上 CDN 才能發揮最大的效用,但“不 緩存的原始獲取”仍然具有性能優勢:客戶端連接終止于附近的服務器,從而 顯著減少握手延遲。相應地, CDN 或你的代理服務器可以維護一個“熱連接池” ( warm connection pool),通過它將數據轉發給原始服務器,同時做到對客戶端快速響應。
事實上,作為附加的一個優化層, CDN 提供商在連接兩端都會使用鄰近服務器! 客戶端連接終止于鄰近 CDN 節點,該節點將請求轉發到與對端服務器鄰近的 CDN 節點,之后請求才會被路由到原始服務器。 CDN 網絡中多出來這一跳,可以讓數 據在優化的 CDN 骨干網中尋路,從而進一步減少客戶端與服務器之間的延遲。
### 4.7.3 會話緩存與無狀態恢復
無論什么情況下,在接近用戶的地方終止連接都有助于減少延遲,但有延遲終歸快不 過沒有延遲。啟用 TLS 會話緩存和無狀態恢復可以完全消除“回頭客”的往返時間。
SSL 2.0 引入的會話標識符機制是 TLS 會話緩存的基礎,目前已經得到大多數客戶 端和瀏覽器的廣泛支持。然而,如果你在自己的服務器上配置 SSL/TLS,千萬不能 主觀認為該機制默認是開啟的。實際上,在大多數服務器的默認配置下它是禁用的。 為此,應該仔細檢查和驗證自己的配置:
- 支持多進程或工作進程的服務器應該使用共享的會話緩存;
- 共享的會話緩存的大小應該根據流量調整;
- 應該設置會話超時時間;
- 在多臺服務器并存的情況下,把相同的客戶端 IP 或相同的 TLS 會話 ID 路由到同 一臺服務器可以最好地利用會話緩存;
- 在不適宜使用“單一”負載均衡策略的情況下,應該為多臺服務器配置共享緩存, 以便最好地利用會話緩存;
- 檢查和監控 SSL/TLS 會話緩存的使用情況,以之作為性能調優的依據。 傳輸層安全
此外,如果客戶端和服務器都支持會話記錄單,則所有會話數據都將保存在客戶端, 上述步驟就都不需要了(問題一下子就簡單了很多)!不過,由于會話記錄單還是 相對新的 TLS 擴展,并非所有客戶端都支持它。實踐中,為了取得最優結果,應該 做好兩手準備:在支持的客戶端中使用會話記錄單,而在不支持的客戶端中使用會 話標識符。這兩種手段不會相互干擾,而是會很好地協同工作。
### 4.7.4 TLS記錄大小
所有通過 TLS 交付的應用數據都會根據記錄協議傳輸(圖 4-8)。每條記錄的上限 為 16 KB,視選擇的加密方式不同,每條記錄還可能額外帶有 20 到 40 字節的首部、 MAC 及可選的填充信息。如果記錄可以封裝在一個 TCP 分組內,則還會給它增加 相應的 IP 和 TCP 字段,即 20 字節的 IP 首部和 20 字節的 TCP 首部(無選項)。結 果,每條記錄的大小就變成了 60 到 100 字節。由于 MTU 通常為 1500 字節,因此 分組占比最小的情況下只相當于幀大小的 6%。
記錄越小,分幀浪費越大。但簡單地把記錄增大到上限( 60 KB)也不一定好!如 果記錄要分成多個 TCP 分組,那 TLS 層必須等到所有 TCP 分組都到達之后才能解 密數據(圖 4-10)。只要有 TCP 分組因擁塞控制而丟失、失序或被節流,那就必須 將相應 TLS 記錄的分段緩存起來,從而導致額外的延遲。實踐中,這種延遲會造成 瀏覽器性能顯著下降,因為瀏覽器傾向于逐字節地讀取數據。

圖 4-10: WireShark 的截圖,其中 11 211 字節的 TLS 記錄被分成了 8 個 TCP 段
小記錄會造成浪費,大記錄會導致延遲。因此,記錄到底多大合適沒有唯一“正 確”的答案。不過對于在瀏覽器中運行的 Web 應用來說,倒是有一個值得推薦的做 法:每個 TCP 分組恰好封裝一個 TLS 記錄,而 TLS 記錄大小恰好占滿 TCP 分配MSS( Maximum Segment Size,最大段大小)。換句話說,一方面不要讓 TLS 記錄 分成多個 TCP 分組,另一方面又要盡量在一條記錄中多發送數據。以下數據可作為 確定最優 TLS 記錄大小的參考:
- IPv4 幀需要 20 字節, IPv6 需要 40 字節;
- TCP 幀需要 20 字節;
- TCP 選項需要 40 字節(時間戳、 SACK 等)。
假設常見的 MTU 為 1500 字節,則 TLS 記錄大小在 IPv4 下是 1420 字節,在 IPv6 下是 1400 字節。為確保向前兼容,建議使用 IPv6 下的大小: 1400 字節。當然,如 果 MTU 更小,這個值也要相應調小。
可惜的是,我們不能在應用層控制 TLS 記錄大小。 TLS 記錄大小通常是一個設置, 甚至是 TLS 服務器上的編譯時常量或標志。要了解具體如何設置這個值,請參考服 務器文檔。
>*如果服務器要處理大量 TLS 連接,那么關鍵的優化是把每個連接占用的內 存量控制在最小。默認情況下, OpenSSL 等常用的庫會給每個連接分配 50 KB 空間,但正像設置記錄大小一樣,有必要查一查文檔或者源代碼,然 后再決定如何調整這個值。谷歌的服務器把 OpenSSL 緩沖區的大小減少到 了大約 5 KB。*
### 4.7.5 TLS壓縮
TLS 還有一個內置的小功能,就是支持對記錄協議傳輸的數據進行無損壓縮。壓縮 算法在 TLS 握手期間商定,壓縮操作在對記錄加密之前執行。然而,出于如下原 因,實踐中往往需要禁用服務器上的 TLS 壓縮功能:
- 2012 年公布的“ CRIME”攻擊會利用 TLS 壓縮恢復加密認證 cookie,讓攻擊者 實施會話劫持;
- 傳輸級的 TLS 壓縮不關心內容,可能會再次壓縮已經壓縮過的數據(圖像、視頻, 等等)。
雙重壓縮會浪費服務器和客戶端的 CPU 時間,而且暴露的安全漏洞也很嚴重,因此 請禁用 TLS 壓縮。實踐中,大多數瀏覽器會禁用 TLS 壓縮,但即便如此你也應該 在服務器的配置中明確禁用它,以保護用戶的利益。
>*雖然不能使用 TLS 壓縮,但應該使用服務器的 Gzip 設置壓縮所有文本資 源,同時對圖像、視頻、音頻等媒體采用最合適的壓縮格式。 *
### 4.7.6 證書鏈的長度
驗證信任鏈需要瀏覽器遍歷鏈條中的每個節點,從站點證書開始遞歸驗證父證書, 直至信任的根證書。因此,優化的首要工作就是檢查服務器在握手時沒有忘記包含 所有中間證書。如果忘記了包含中間證書,雖然很多瀏覽器可以正常工作,但它們 會暫停驗證并自己獲取中間證書,驗證之后再繼續。此時很可能需要進行新 DNS 查 找、建立 TCP 連接、發送 HTTP GET 請求,導致握手多花幾百 ms 時間。
>*瀏覽器怎么知道到哪里去找證書呢?子證書中通常包含父證書的 URL。*
另一方面,還要確保信任鏈中不包含不必要的證書!或更一般化地講,應該確保證 書鏈的長度最小。我們在 4.2 節“ TLS 握手”中介紹過,服務器證書是在握手期間 發送的,而發送證書使用的很可能是一個處于 2.2.2 節“慢啟動”算法初始階段的 新 TCP 連接。如果證書鏈長度超過了 TCP 的初始擁塞窗口(圖 4-11),那我們無意 間就會讓握手多了一次往返:證書長度超過擁塞窗口,從而導致服務器停下來等待 客戶端的 ACK 消息。

圖 4-11: WireShark 的截圖顯示的 5323 字節的 TLS 證書鏈
圖 4-11 所示的證書鏈超過了 5 KB,也超過了大多數舊版本瀏覽器的初始擁塞窗口 大小,因此會在握手期間增加一次額外的往返。對此,可以通過增大擁塞窗口來解 決,參見 2.2.2 節最后的“增大 TCP 的初始擁塞窗口”部分。此外,就是要看一看 能否減少要發送的證書大小了。
- 盡量減少中間證書頒發機構的數量。理想情況下,發送的證書鏈應該只包含兩個 證書:站點證書和中間證書頒發機構的證書。把這一條作為選擇證書頒發機構的 標準。第三個證書,也就是根證書頒發機構的證書,已經包含在瀏覽器內置的信 任名單中了,不用發送。
- 很多站點會在證書鏈中包含根證書頒發機構的證書, 這是完全沒有必要的。如果瀏 覽器的信任名單中沒有該根證書,那說明它是不被信任的,即便發送它也無濟于事。
- 理想的證書鏈應該在 2 KB 或 3 KB 左右,同時還能給瀏覽器提供所有必要的信息, 避免不必要的往返或者對證書本身額外的請求。優化 TLS 握手可以消除關鍵的性 能瓶頸,因為每個新 TLS 連接都要經歷同樣的延遲。
### 4.7.7 OCSP封套
每個新 TLS 連接都要求瀏覽器驗證發送過來的證書鏈的簽名。然而,不要忘了還 有一步:瀏覽器也需要驗證證書沒有被撤銷。為此,瀏覽器可能會定期下載并緩存 4.5.1 節提到的證書頒發機構發布的“證書撤銷名單( CRL)”,而且還可能需要分派 發送一個 4.5.2 節提到的“在線證書狀態協議( OCSP)”請求,以便實時驗證。遺 憾的是,瀏覽器在這時候的行為差別很大。
- 某些瀏覽器會使用自己的更新機制推送更新的 CRL 名單,而不會按需發送請求。
- 某些瀏覽器可能只會針對擴展驗證證書( EV 證書)進行實時 OCSP 和 CRL 檢查。
- 某些瀏覽器可能會在上述任何一種方式下阻塞 TLS 握手,有些則不會,具體情況 取決于開發商、平臺和瀏覽器版本。
這里的情況很復雜,也沒有最好的解決方案。不過,在某些瀏覽器中還是可以采用 一個叫做 OCSP 封套( OCSP stapling)的優化措施:服務器可以在證書鏈中包含 (封套)證書頒發機構的 OCSP 響應,讓瀏覽器跳過在線查詢。把查詢 OCSP 操作 轉移到服務器可以讓服務器緩存簽名的 OCSP 響應,從而節省很多客戶端的請求。 與此同時,還要注意一些情況。
- OCSP 響應從 400 字節到 4000 字節不等。把這么大的響應封套在證書鏈里照樣 會造成 TCP 擁塞窗口溢出,因此要關注整體大小。
- 只能包含一個 OCSP 響應,即在沒有緩存的情況下,瀏覽器對其他中間證書可能 仍然需要發送 OCSP 請求。
最后,要啟用 OCSP 封套,還需要服務器支持才行。好在, NginX、 Apache 和 IIS 等服務器都可以通過配置支持 OCSP 封套。對于其他服務器,請參考文檔中的說明。
### 4.7.8 HTTP嚴格傳輸安全( HSTS)
HTTP 嚴格傳輸安全( HSTS, Strict Transport Security)是一種安全策略機制,它能讓 服務器通過簡單的 HTTP 首部( 如 Strict-Transport-Security: max-age=31536000) 對適用的瀏覽器聲明訪問規則。具體來講,它可以讓用戶代理遵從如下規則:
- 所有對原始服務器的請求都通過 HTTPS 發送;
- 所有不安全的鏈接和客戶端請求在發送之前都應該在客戶端自動轉換為 HTTPS;
- 萬一證書有錯誤,則顯示錯誤消息,用戶不能回避警告;
- max-age 以秒為單位指定 HSTS 規則集的生存時間(例如, max-age=31536000 等于 緩存 365 天);
- 用戶代理可以根據指令在指定的證書鏈中記住(“印下”)某主機的指紋,以便將 來訪問時使用,從而有效限制證書頒發機構在特定時間(由 max-age 指定)內可 頒發證書的范圍。(這一項是可選的。)
事實上, HSTS 會把原始服務器轉換為只處理 HTTPS 的目標服務器,從而確保應用 不會因各種主動或被動攻擊給用戶造成損失。從性能角度說, HSTS 通過把責任轉 移到客戶端,讓客戶端自動把所有鏈接重寫為 HTTPS,消除了從 HTTP 到 HTTPS 的重定向損失。
>*到 2013 年初,支持 HSTS 的瀏覽器有 Firefox 4+、 Chrome 4+、 Opera 12+ 和 Android 平臺的 Chrome 和 Firefox。要了解最新的支持情況,請訪問: http://caniuse.com/stricttransportsecurity。*
## 4.8 性能檢查清單
作為應用開發人員,事實上你接觸不到 TLS 的這些復雜性。只要別把頁面中的 HTTP 和 HTTPS 內容混為一談,那你的應用就可以在這兩種情況下都順暢運行。然 而,應用的整體性能卻會受到服務器底層配置的影響。
好在,只要想到優化,任何時候都不算晚。而且一旦優化到位,所有與服務器的新 連接都將受益無窮!下面是一個簡單的檢查清單:
- 要最大限制提升 TCP 性能,請參考 2.5 節“針對 TCP 的優化建議”;
- 把 TLS 庫升級到最新版本,在此基礎上構建(或重新構建)服務器;
- 啟用并配置會話緩存和無狀態恢復;
- 監控會話緩存的使用情況并作出相應調整;
- 在接近用戶的地方完成 TLS 會話,盡量減少往返延遲;
- 配置 TLS 記錄大小,使其恰好能封裝在一個 TCP 段內;
- 確保證書鏈不會超過擁塞窗口的大小;
- 從信任鏈中去掉不必要的證書,減少鏈條層次;
- 禁用服務器的 TLS 壓縮功能;
- 啟用服務器對 SNI 的支持;
- 啟用服務器的 OCSP 封套功能;
- 追加 HTTP 嚴格傳輸安全首部。
## 4.9 測試與驗證
最后,要驗證和測試你的配置,可以使用 Qualys SSL Server Test( https://www. ssllabs.com/ssltest/)等在線服務來掃描你的服務器,以發現常見的配置和安全漏洞。 此外,最好熟練掌握 openssl 命令行工具,通過它來檢查整個握手和本地服務器配 置情況:
```
$> openssl s_client -state -CAfile startssl.ca.crt -connect igvita.com:443
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=2 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Certification Authority
verify return:1
depth=1 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA verify return:1
depth=0 /description=ABjQuqt3nPv7ebEG/C=US /CN=www.igvita.com/emailAddress=ilya@igvita.com
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server done A ?
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read finished A
---
Certificate chain ?
0 s:/description=ABjQuqt3nPv7ebEG/C=US /CN=www.igvita.com/emailAddress=ilya@igvita.com
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA
1 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Certification Authority
---
Server certificate -----BEGIN CERTIFICATE-----
... snip ...
---
No client certificate CA names sent
---
SSL handshake has read 3571 bytes and written 444 bytes ?
---
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
Session-ID: 269349C84A4702EFA7 ... ?
Session-ID-ctx:
Master-Key: 1F5F5F33D50BE6228A ...
Key-Arg : None
Start Time: 1354037095
Timeout : 300 (sec)
Verify return code: 0 (ok)
```
? 客戶端完成對接收到的證書鏈的驗證
? 接收到的證書鏈( 2 個證書)
? 接收到證書鏈的大小
? 對有狀態 TLS 恢復發送的會話標識符
在上面的例子中,我們連接到 igvita.com 默認的 TLS 端口( 443),并進行了 TLS 握 手。因為 s_client 假設沒有根證書,所以我們手工把路徑指定為 StartSSL Certificate Authority 的根證書,這一點很重要。你的瀏覽器內置了 StartSSL 的根證書,因此可 以驗證這個信任鏈,但 s_client 沒有依賴于此。如果在這里忽略根證書,應該會在 日志中看到驗證錯誤。
通過檢查證書鏈,我們發現服務器發送了兩個證書,累計起來是 3571 字節,差不 多相當于 3~4 個 TCP 初始擁塞窗口的大小。這里要注意不能超過擁塞窗口大小,否 則就要考慮增大服務器上 cwnd 的值。最后,我們可以看到協商后的 SSL 會話變量: 選擇的協議、加密套件、密鑰等。還可以看到服務器為當前會話發送的會話標識符, 這個標識符在將來恢復時會用到。