透視HTTP協議-HTTPS握手及性能優化

HTTPS創建鏈接

當你在瀏覽器地址欄裏鍵入」https「開頭的URI,再按下回車,會發生什麼呢?算法

瀏覽器首先要從URI裏提取出協議名和域名。由於協議名是」https「,因此瀏覽器就知道了端口號是默認的443,它再用DNS解析域名,獲得目標的IP地址,而後就可使用三次握手與網站創建TCP鏈接了。瀏覽器

在HTTP協議裏,創建鏈接後,瀏覽器會當即發送請求報文。但如今是HTTPS協議,它須要再用另一個」握手「過程,在TCP上創建安全鏈接,以後纔是收發HTTP報文。緩存

這個」握手「過程與TCP有些相似,是HTTPS和TLS協議裏最重要、最核心的部分,懂了它,你就能夠自豪地說本身」掌握了HTTPS「。安全

TLS協議的組成

TLS包含幾個子協議,比較經常使用的有記錄協議、警報協議、握手協議、變動密碼規範協議等:性能優化

  • 記錄協議(Record Protocol)規定了TLS收發數據的基本單位:記錄(record)。它有點像是TCP裏的segment,全部的其餘子協議都須要經過記錄協議發出。但多個記錄數據能夠在一個TCP包裏一次性發出,也並不須要像TCP那樣返回ACK。
  • 警報協議(Alert Protocol)的職責是向對方發出警報信息,有點像是HTTP協議裏的狀態碼。好比,protocol_version就是不支持舊版本,bad_certificate就是證書有問題,收到警報後另外一方能夠選擇繼續,也能夠當即終止鏈接。
  • 握手協議(Handshake Protocol)是TLS裏最複雜的子協議,要比TCP的SYN/ACK複雜的多,瀏覽器和服務器會在握手過程當中協商TLS版本號、隨機數、密碼套件等信息,而後交換證書和密鑰參數,最終雙方協商獲得會話密鑰,用於後續的混合加密系統。
  • 變動密碼規範協議(Change Cipher Spec Protocol),它很是簡單,就是一個」通知「,告訴對方,後續的數據都將使用加密保護。那麼反過來,在它以前,數據都是明文的。

下面的這張圖簡要地描述了TLS的握手過程,其中每個」框「都是一個記錄,多個記錄組合成一個TCP包發送。因此,最多通過兩次消息往返(4個消息)就是完成握手,而後就能夠在安全的通訊環境裏發送HTTP報文,實現HTTPS協議。bash

ECDHE握手過程

握手的詳細圖以下:服務器

在TCP創建鏈接以後,瀏覽器會首先發一個」Client Hello「消息,也就是跟服務器」打招呼「。裏面有客戶端的版本號、支持的密碼套件,還有一個隨機數(Client Random),用於後續生成會話密鑰。網絡

Handshake Protocol: Client Hello
    Version: TLS 1.2 (0x0303)
    Random: 1cbf803321fd2623408dfe…
    Cipher Suites (17 suites)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
複製代碼

這個的意思就是:「我這邊有這些這些信息,你看看哪些是能用的,關鍵的隨機數可得留着」session

做爲「禮尚往來」,服務器收到「Client Hello」後,會返回一個「Server Hello」消息。把版本號對一下,也給出一個隨機數(Server Random),而後從客戶端的列表裏選一個做爲本次通訊使用的密碼套件,在這裏它選擇了「TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384」。dom

Handshake Protocol: Server Hello
    Version: TLS 1.2 (0x0303)
    Random: 0e6320f21bae50842e96…
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
複製代碼

這個的意思就是:「版本號對上了,能夠加密,你的密碼套件挺多,我選一個最合適的吧,用橢圓曲線加RSA、AES、SHA384。我也給你一個隨機數,你也得留着。」

而後,服務器爲了證實本身的身份,就把證書也發給了客戶端(Server Certificate)。

接下來是一個關鍵的操做,由於服務器選擇了ECDHE算法,因此它會在證書後發送「Server Key Exchange」消息,裏面是橢圓曲線的公鑰(Server Params),用來實現密鑰交換算法,再加上本身的私鑰簽名認證。

Handshake Protocol: Server Key Exchange
    EC Diffie-Hellman Server Params
        Curve Type: named_curve (0x03)
        Named Curve: x25519 (0x001d)
        Pubkey: 3b39deaf00217894e...
        Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
        Signature: 37141adac38ea4...
複製代碼

這至關於說:「剛纔我選的密碼套件有點複雜,因此再給你個算法的參數,和剛纔的隨機數同樣有用,別丟了。爲了防止別人冒充,我又蓋了個章。」

以後是「Server Hello Done」消息,服務器說:「個人信息就是這些,打招呼完畢。」

這樣第一個消息往返就結束了(兩個TCP包),結果是客戶端和服務器經過明文共享了三個信息:Client Random、Server Random和Server Params。

客戶端這時也拿到了服務器的證書,開始走證書鏈逐級驗證,確認證書的真實性,再用證書公鑰驗證簽名,就確認了服務器的身份:「剛纔跟我打招呼的不是騙子,能夠接着往下走。」

而後,客戶端按照密碼套件的要求,也生成一個橢圓曲線的公鑰(Client Params),用「Client Key Exchange」消息發給服務器。

Handshake Protocol: Client Key Exchange
    EC Diffie-Hellman Client Params
        Pubkey: 8c674d0e08dc27b5eaa…
複製代碼

如今客戶端和服務器手裏都拿到了密鑰交換算法的兩個參數(Client Params、Server Params),就用ECDHE算法一陣算,算出了一個新的東西,叫「Pre-Master」,其實也是一個隨機數。

如今客戶端和服務器手裏有了三個隨機數:Client Random、Server Random和Pre-Master。用這三個做爲原始材料,就能夠生成用於加密會話的主密鑰,叫「Master Secret」。而黑客由於拿不到「Pre-Master」,因此也就得不到主密鑰。

主密鑰有48字節,但它也不是最終用於通訊的會話密鑰,還會再用PRF擴展出更多的密鑰,好比客戶端發送用的會話密鑰(client_write_key)、服務器發送用的會話密鑰(server_write_key)等等,避免只用一個密鑰帶來的安全隱患。

有了主密鑰和派生的會話密鑰,握手就快結束了。客戶端發一個」Change Cipher Spec「,而後再發一個」Finished「消息,把以前全部發送的數據作個摘要,再加密一下,讓服務器作個驗證。

意思就是告訴服務器:「後面都改用對稱算法加密通訊了啊,用的就是打招呼時說的 AES,加密對不對還得你測一下。」

服務器也是一樣的操做,發「Change Cipher Spec」和「Finished」消息,雙方都驗證加密解密 OK,握手正式結束,後面就收發被加密的 HTTP 請求和響應了。

RSA握手過程

剛纔說的實際上是現在主流的 TLS 握手過程,這與傳統的握手有兩點不一樣。

第一個,使用 ECDHE 實現密鑰交換,而不是 RSA,因此會在服務器端發出「Server Key Exchange」消息。

第二個,由於使用了 ECDHE,客戶端能夠不用等到服務器發回「Finished」確認握手完畢,當即就發出 HTTP 報文,省去了一個消息往返的時間浪費。這個叫「TLS False Start」,意思就是「搶跑」,和「TCP Fast Open」有點像,都是不等鏈接徹底創建就提早發應用數據,提升傳輸的效率。

流程如上圖所示,大致沒有變,只是」Pre-Master「再也不須要用算法生成,而是客戶端直接生成隨機數,而後用服務器的公鑰加密,經過」Client Key Exchange「消息發給服務器。服務器再用私鑰解密,這樣雙方也實現了共享三個隨機數,就能夠生成主密鑰。

雙向認證

不過上面說的是「單向認證」握手過程,只認證了服務器的身份,而沒有認證客戶端的身份。這是由於一般單向認證經過後已經創建了安全通訊,用帳號、密碼等簡單的手段就可以確認用戶的真實身份。

但爲了防止帳號、密碼被盜,有的時候(好比網上銀行)還會使用 U 盾給用戶頒發客戶端證書,實現「雙向認證」,這樣會更加安全。

雙向認證的流程也沒有太多變化,只是在「Server Hello Done」以後,「Client Key Exchange」以前,客戶端要發送「Client Certificate」消息,服務器收到後也把證書鏈走一遍,驗證客戶端的身份。

TLS1.3特性解析

首先咱們來了解下TLS1.3的三個主要改進目標:兼容、安全與性能

1. 最大化兼容性

在早期的實驗中發現,一旦變動了記錄頭字段裏的版本號,也就是由0x303(TLS1.2)改成0x304(TLS1.3)的話,大量的代理服務器、網關都沒法正確處理,最終致使TLS握手失敗。

爲了保證這些被普遍部署的」老設備「可以繼續使用,避免新協議帶來的」衝擊「,TLS1.3不得不做出妥協,保持現有的記錄格式不變,經過」假裝「來實現兼容,使得TLS1.3看上去」像是「TLS1.2。

那麼,如何區分1.2和1.3呢?

這要用到一個新的擴展協議(Extension Protocol),經過在記錄末尾添加一系列的」擴展字段「來增長新的功能,老版本的TLS不認識它能夠直接忽略,這就實現了」後向兼容「。

在記錄頭的Version字段被兼容性」固定「的狀況下,只要是TLS1.3協議,握手的」Hello「消息後面就必須有」supported_Versions「擴展,它標記了TLS的版本號,使用它就能區分新舊協議。

強化安全

TLS1.3在協議裏修補了TLS1.2的一些不安全因素:

  • 僞隨機數函數由PRF升級爲HKDF(HMAC-based Extract-and-Expand Key Derivation Function);
  • 明確禁止在記錄協議裏使用壓縮;
  • 廢除了RC四、DES對稱加密算法;
  • 廢除了ECB、CBC等傳統分組模式;
  • 廢除了MD五、SHA一、SHA-224摘要算法;
  • 廢除了RSA、DH密鑰交換算法和許多命名曲線。 通過這一番「減肥瘦身」以後,TLS1.3 裏只保留了 AES、ChaCha20 對稱加密算法,分組模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA25六、SHA384,密鑰交換算法只有 ECDHE 和 DHE,橢圓曲線也被「砍」到只剩 P-256 和 x25519 等 5 種。致使如今的TLS1.3裏只有5個套件,不管是客戶端仍是服務器都不會再犯」選擇困難症「了。

這裏說明下廢除RSA和DH密鑰交換算法的緣由。

瀏覽器默認會使用ECDHS而不是RSA作密鑰交換,是由於它不具備」前向安全「(Forward Secrecy)。一旦私鑰泄露或被解出來,那麼黑客就可以使用私鑰解密出以前全部報文的」Pre-Master「,再算出會話密鑰,解出全部密文。這就是所謂的「今日截獲,明日解密」。

而ECDHE算法在每次握手時都會生成一對臨時的公鑰和私鑰,每次通訊的密鑰對都是不一樣的,也就是「一次一密」,即便黑客花大力氣解出了這一次的會話密鑰,也只是此次通訊被攻擊,以前的歷史消息不會受到影響,仍然是安全的。

因此如今主流的服務器和瀏覽器在握手階段都已經再也不使用RSA,改用ECDHE,而TLS1.3在協議裏明確廢除RSA和DH則在標準層面保證了「前向安全」。

提高性能

HTTPS創建鏈接時除了要作TCP握手,還要作TLS握手,在1.2中會多花兩個消息往返(2-RTT),可能致使幾十毫秒甚至上百毫秒的延遲,在移動網絡中延遲還會更嚴重。

如今由於密碼套件大幅度簡化,也就沒有必要再像之前那樣走複雜的協商流程了。TLS1.3壓縮了之前的「Hello」協商過程,刪除了「Key Exchange」消息,把握手時間減小到了「1-RTT」,效率提升了一倍。

其實具體的作法仍是利用了擴展。客戶端在「Client Hello」消息裏直接用「supported_groups」帶上支持的曲線,好比P-25六、x25519,用「key_share」帶上曲線對應的客戶端公鑰參數,用「signature_algorithms」帶上簽名算法。

服務器收到後在這些擴展裏選定一個曲線和參數,再用「key_share」擴展返回服務器這邊的公鑰參數,就實現了雙方的密鑰交換。

握手分析

TLS1.3握手的過程以下圖:

在TCP創建鏈接以後,瀏覽器首先仍是發一個「Client Hello」。

由於1.3的消息兼容1.2,因此開頭的版本號、支持的密碼套件和隨機數(Client Random)結構都是同樣的(不過這時的隨機數是32個字節)。

Handshake Protocol: Client Hello
    Version: TLS 1.2 (0x0303)
    Random: cebeb6c05403654d66c2329…
    Cipher Suites (18 suites)
        Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
        Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
        Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
    Extension: supported_versions (len=9)
        Supported Version: TLS 1.3 (0x0304)
        Supported Version: TLS 1.2 (0x0303)
    Extension: supported_groups (len=14)
        Supported Groups (6 groups)
            Supported Group: x25519 (0x001d)
            Supported Group: secp256r1 (0x0017)
    Extension: key_share (len=107)
        Key Share extension
            Client Key Share Length: 105
            Key Share Entry: Group: x25519
            Key Share Entry: Group: secp256r1
複製代碼

注意「Client Hello」裏的擴展,「supported_versions」表示這是TLS1.3,「supported_groups」是支持的曲線,「key_share」是曲線對應的參數。

服務器收到「Client Hello」一樣返回「Server Hello」消息,仍是要給出一個隨機數(Server Random)和選定密碼套件。

Handshake Protocol: Server Hello
    Version: TLS 1.2 (0x0303)
    Random: 12d2bce6568b063d3dee2…
    Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
    Extension: supported_versions (len=2)
        Supported Version: TLS 1.3 (0x0304)
    Extension: key_share (len=36)
        Key Share extension
            Key Share Entry: Group: x25519, Key Exchange length: 32
複製代碼

表面上看跟TLS1.2是同樣的,重點是後面的擴展。「supported_versions」裏確認使用的是TLS1.3,而後在「key_share」擴展帶上曲線和對應的公鑰參數。

這時只交換了兩條消息,客戶端和服務器就拿到了四個共享信息:Client Random和Server Random、Client Params和Server Params,兩邊就能夠各自用ECDHE算出「Pre-Master」,再用HKDF生成主密鑰「Master Secret」,效率比TLS1.2提升了一大截。

在算出主密鑰後,服務器馬上發出「Change Cipher Spec」消息,比TLS1.2提前進入加密通訊,後面的證書等就都是加密的了,減小了握手時的明文信息泄露。

這裏TLS1.3還有一個安全強化措施,多了個「Certificate Verify」消息,用服務器的私鑰把前面的曲線、套件、參數等握手數據加了簽名,做用和「Finished」消息差很少。但因爲是私鑰簽名,因此強化了身份認證和防篡改。

這兩個「Hello」消息以後,客戶端驗證服務器證書,再發「Finished」消息,就正式完成了握手,開始收發HTTP報文。

HTTPS的優化

HTTPS鏈接大體能夠劃分紅兩個部分,第一個是創建鏈接時的非對稱加密握手,第二個是握手後的對稱加密報文傳輸。

因爲目前流行的AES、ChaCha20性能都很好,還有硬件優化,報文傳輸的性能損耗能夠說是很是地小,小到幾乎能夠忽略不計了。因此,一般所說的「HTTPS鏈接慢」指的就是剛開始創建鏈接的那段時間。

而TLS影響性能部分以下圖:

性能優化分硬件優化和軟件優化。軟件方面的優化還能夠再分紅兩部分:一個是軟件升級,一個是協議優化。

軟件升級實施起來比較簡單,就是把如今正在使用的軟件儘可能升級到最新版本。

協議優化

應當儘可能採用TLS1.3,它大幅度簡化了握手的過程,徹底握手只要1-RTT,並且更加安全。 若是暫時不能升級到1.3,只能用1.2,那麼握手時使用的密鑰交換協議應當儘可能選用橢圓曲線的ECDHE算法。它不只運算速度快,安全性高,還支持「False Start」,可以把握手的消息往返由 2-RTT 減小到 1-RTT,達到與 TLS1.3 相似的效果。

另外,橢圓曲線也要選擇高性能的曲線,最好是 x25519,次優選擇是 P-256。對稱加密算法方面,也能夠選用「AES_128_GCM」,它能比「AES_256_GCM」略快一點點。

在 Nginx 裏能夠用「ssl_ciphers」「ssl_ecdh_curve」等指令配置服務器使用的密碼套件和橢圓曲線,把優先使用的放在前面,例如:

ssl_ciphers   TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:EECDH+CHACHA20;
ssl_ecdh_curve              X25519:P-256;
複製代碼

證書優化

這裏就有兩個優化點,一個是證書傳輸,一個是證書驗證。

服務器的證書能夠選擇橢圓曲線(ECDSA)證書而不是 RSA 證書,由於 224 位的 ECC 至關於 2048 位的 RSA,因此橢圓曲線證書的「個頭」要比 RSA 小不少,即可以節約帶寬也能減小客戶端的運算量,可謂「一箭雙鵰」。

客戶端的證書驗證實際上是個很複雜的操做,除了要公鑰解密驗證多個證書籤名外,由於證書還有可能會被撤銷失效,客戶端有時還會再去訪問 CA,下載 CRL 或者 OCSP 數據,這又會產生 DNS 查詢、創建鏈接、收發數據等一系列網絡通訊,增長好幾個 RTT。

CRL(Certificate revocation list,證書吊銷列表)由 CA 按期發佈,裏面是全部被撤銷信任的證書序號,查詢這個列表就能夠知道證書是否有效。但 CRL 由於是「按期」發佈,就有「時間窗口」的安全隱患,並且隨着吊銷證書的增多,列表會愈來愈大,一個 CRL 常常會上 MB。想象一下,每次須要預先下載幾 M 的「無用數據」才能鏈接網站,實用性實在是過低了。

因此,如今 CRL 基本上不用了,取而代之的是 OCSP(在線證書狀態協議,Online Certificate Status Protocol),向 CA 發送查詢請求,讓 CA 返回證書的有效狀態。但 OCSP 也要多出一次網絡請求的消耗,並且還依賴於 CA 服務器,若是 CA 服務器很忙,那響應延遲也是等不起的

因而又出來了一個「補丁」,叫「OCSP Stapling」(OCSP 裝訂),它可讓服務器預先訪問 CA 獲取 OCSP 響應,而後在握手時隨着證書一塊兒發給客戶端,免去了客戶端鏈接 CA 服務器查詢的時間。

會話複用

咱們再回想一下 HTTPS 創建鏈接的過程:先是 TCP 三次握手,而後是 TLS 一次握手。這後一次握手的重點是算出主密鑰「Master Secret」,而主密鑰每次鏈接都要從新計算,未免有點太浪費了,若是可以把「辛辛苦苦」算出來的主密鑰緩存一下「重用」,不就能夠免去了握手和計算的成本了嗎?

這種作法就叫「會話複用」(TLS session resumption),和 HTTP Cache 同樣,也是提升 HTTPS 性能的「大殺器」,被瀏覽器和服務器普遍應用。

會話複用分兩種,第一種叫「Session ID」,就是客戶端和服務器首次鏈接後各自保存一個會話的 ID 號,內存裏存儲主密鑰和其餘相關的信息。當客戶端再次鏈接時發一個 ID 過來,服務器就在內存裏找,找到就直接用主密鑰恢復會話狀態,跳過證書驗證和密鑰交換,只用一個消息往返就能夠創建安全通訊。

會話票證

「Session ID」是最先出現的會話複用技術,也是應用最廣的,但它也有缺點,服務器必須保存每個客戶端的會話數據,對於擁有百萬、千萬級別用戶的網站來講存儲量就成了大問題,加劇了服務器的負擔。

因而,又出現了第二種「Session Ticket」方案。它有點相似 HTTP 的 Cookie,存儲的責任由服務器轉移到了客戶端,服務器加密會話信息,用「New Session Ticket」消息發給客戶端,讓客戶端保存。重連的時候,客戶端使用擴展「session_ticket」發送「Ticket」而不是「Session ID」,服務器解密後驗證有效期,就能夠恢復會話,開始加密通訊。

預共享密鑰

「False Start」「Session ID」「Session Ticket」等方式只能實現 1-RTT,而 TLS1.3 更進一步實現了「0-RTT」,原理和「Session Ticket」差很少,但在發送 Ticket 的同時會帶上應用數據(Early Data),免去了 1.2 裏的服務器確認步驟,這種方式叫「Pre-shared Key」,簡稱爲「PSK」。

Q&A

Q:密碼套件裏的那些算法分別在握手過程當中起了什麼做用?

Q:你能完整地描述一下 RSA 的握手過程嗎?

Q:你能畫出雙向認證的流程圖嗎?

Q:TLS1.3 裏的密碼套件沒有指定密鑰交換算法和簽名算法,那麼在握手的時候會不會有問題呢?

Q:結合 RSA 握手過程,解釋一下爲何 RSA 密鑰交換不具備「前向安全」。

Q:TLS1.3 的握手過程與 TLS1.2 的「False Start」有什麼異同?

Q:你能比較一下「Session ID」「Session Ticket」「PSK」這三種會話複用手段的異同嗎?

相關文章
相關標籤/搜索