1. 程式人生 > >Https:從tcp建立連線到https接收到第一個資料包,到底發生了什麼?

Https:從tcp建立連線到https接收到第一個資料包,到底發生了什麼?

https 建立連線過程

Abstract

https 是基於http 和 ssl(安全套接字層) 的安全傳輸協議,使用ssl 協議作為會話層協議,這裡通過這篇文章結合抓包來看一下具體過程

About SSL / TLS

這個協議最早是由網景公司 開發,但是隨著網景的沒落,現在由ietf負責維護,最初的版本也已經重新冠名(re-banded)tls(安全傳輸層協議) 1.0(1999年)。因此現在大部分協議是基於TLS的,儘管是相似的東西。

https 建立簡介過程

client say hello

  • 客戶端瀏覽器發起握手請求,在ssl 協議頭的 content type 設定為 handshake(0x16)。這表明這是一個handshake record。
  • 緊接著的2位元組是ssl 的版本號。
  • 整個handshake 內容又分為幾個部分的訊息

    • Random 4位元組的unix時間格式的 UTC時間(協調世界時),後面緊跟28 bit 的隨機數。這個將在後面用到
    • Session ID , 這個在client hello 階段是空,假設我們之前已經與服務端建立過https 連結,那麼這個有可能是一個有意義的數字
    • Cipher Suites 這裡給出了所有瀏覽器支援的加密協議,最頂部的是瀏覽器最期望使用的協議。
    • server_name extension 這裡給出客戶端正嘗試訪問的URL。因為ssl發生在http建立連線之前。而伺服器可能是一個ip對應多個URL的方式,這個時候是無法區分對應的服務的。(nginx的 TLS NSI 支援)

      LVS使用FULLNAT模式,每臺Nginx 機器只有一個IP(內網IP),LVS也是把流量轉到這個IP。如果Nginx想對多個域名使用https,比如兩個域名 wandoujia.com 和 wandoulabs.com ,是可能有問題的。

      事實上,SSL執行在TCP之上(SSL/TLS協議),ssl通過四次握手 和伺服器(這裡是Nginx,LVS純轉發,可忽略)的IP + PORT(443)建立ssl連線,建立連線之後瀏覽器才會傳送HTTP請求。所以在Nginx收到HTTP請求之後才知道Host,才知道轉到哪個server 去處理,所以在SSL連線建立的時候Nginx是不知道用哪個 Server 的SSL配置的,在這種情況下,Nginx會使用它載入到的第一個SSL配置(需驗證)。

      那麼怎麼才能實現多域名的 https 呢,是有辦法的,叫做 TLS Server Name Indication extension(SNI, RFC 6066),它允許瀏覽器在SSL握手的時候傳送請求的server name,也就是 Host,這樣 Nginx 就能找到對應server 的SSL配置。

server hello

server 回覆客戶端 hello 包含2個packet 2551位元組的回覆,包含tls 版本資訊。整個server hello 可以分為3部分消

  • server hello message : 
    • 4byte Unix UTC 時間 和 28 byte 的隨機數
    • 32 位元組session ID, 用於避免完下次全握手
    • 服務端提供的 加密套件。例如: (0x0004)TLS_RSA_WITH_RC4_128_MD5,這代表著 RSA 公鑰演算法、RC4加密演算法、MD5 hash認證函式,
  • Cerfificate Message : 證書訊息,客戶端用於認證server 的證書
  • Server Hello Done 通知client hello done,server 不會要求client 的證書

Checking out the Certificate & Verifying Signatures

  • 客戶端需要驗證server 的證書有效性,來確認是否應該信任 server。首先確認證書尚未過期,同時確認 證書的公鑰是授權的,可以用於私鑰交換。防止發成 中間人攻擊

    because we are implicitly trusting that the people on the certificate trust chain wouldn’t do something bad, like sign a certificate claiming to be from Amazon.com unless it actually was Amazon.com. If an attacker is able to modify your DNS server by using a technique like DNS cache poisoning, you might be fooled into thinking you’re at a trusted site (like Amazon.com) because the address bar will look normal. This last check implicitly trusts certificate authorities to stop these bad things from happening.

    簡單地說就是,我們的金鑰交換過程應該是發生在信任的通道中,如果不是這樣,比如,有人你與伺服器之間,欺騙你與他建立了信任關係(劫持dns 等等方式)。那麼你的認證就是去了意義。因此引入三方認證,避免這種可能性。

  • 緊接著要說的是瀏覽器 如何 驗證證書的合法性

    如果不能保證證書是server 的合法證書,那麼客戶端傳送的資訊就有可能存在被竊聽的危險,因為用此公鑰加密的資料可以被其對應的私鑰擁有者獲取,而該私鑰並不在客戶端所認為的伺服器上。 
    因此可採用一個權威機構進行證書的頒發,所謂證書就是包含了伺服器宣告的公鑰以及組織名稱等資訊,這裡我們只考慮最關鍵的公鑰資訊。該權威機構會對申請證書的組織進行稽核,確保其身份合法,然後將伺服器公鑰資訊釋出給客戶端,客戶端可利用該公鑰與對應的伺服器進行通訊。整個過程可歸納為以下幾步:

    1、伺服器生成一對金鑰,私鑰自己留著,公鑰交給數字證書認證機構(CA)

    2、CA進行稽核,並用CA自己的私鑰對伺服器提供的公鑰進行簽名(參照上文RSA簽名)

    3、客戶端從CA獲取證書(即伺服器端公鑰),用CA的公鑰對簽名的證書進行驗證,比對一致,說明該伺服器公鑰確實是CA頒發的(得此結論有一個前提就是:客戶端的CA公鑰確實是CA的公鑰,即該CA的公鑰與CA對證書進行簽名的私鑰確實是一對。參照上文RSA簽名中所論述的情況),而CA又作為權威機構保證該公鑰的確是伺服器端提供的,從而可以確認該證書中的公鑰確實是合法伺服器端提供的

    注:為保證第3步中提到的前提條件,CA的公鑰必須要安全地轉交給客戶端,因此,CA的公鑰一般來說由瀏覽器開發商內建在瀏覽器的內部。於是,該前提條件在各種信任機制上,基本保證成立。

整個過程涉及2對公私金鑰對,一對由伺服器產生,用於加密,一對由CA產生,用於簽名。 
整個過程還涉及2個信任:客戶端信任CA,CA釋出的證書中的公鑰就是合法伺服器的公鑰。客戶端信任瀏覽器內建的CA公鑰就是與CA私鑰對應的公鑰。

驗證正式是否吊銷可以採用黑名單方式或者OCSP方式。黑名單(CRL)就是定期從CA下載一個名單列表,裡面有吊銷的證書序列號,自己在本地比對一下就行。優點是效率高。缺點是不實時。OCSP是實時連線CA去驗證,優點是實時,缺點是效率不高。

OCSP 全稱線上證書狀態檢查協議 (rfc6960),用來向 CA 站點查詢證書狀態,比如是否撤銷。 通常情況下,瀏覽器使用 OCSP 協議發起查詢請求,CA 返回證書狀態內容,然後瀏覽器接受證書是否可信的狀態。因此如果 server 將證書儲存下來,瀏覽器請求時候直接通過自己的伺服器傳送回去,防止驗證伺服器出問題,還能加快訪問速度。

Pre-Master Secret

到目前為止,我們已經認證了server 並且獲得了server 的可信任公鑰和加密方式(?),但是此時,竊聽者也可以獲得我們我們一樣多的資訊。所以我們需要建立一個隨機金鑰,然而這並不容易

In 1996, researchers figured out that Netscape Navigator 1.1 was using only three sources to seed their pseudo-random number generator (PRNG). The sources were: the time of day, the process id, and the parent process id. As the researchers showed, these “random” sources aren’t that random and were relatively easy to figure out.

早在1996年,研究人員就發現網景瀏覽器1.1 版本使用的3個隨機數種子 非常容易被破解,因此所謂的“隨機金鑰”其實並不隨機,容易被偽造

生成的48位pre master secret隨機值 ,前兩位是ssl 版本

Trading Secrets

客戶端採用server通知的加密方式(服務端經過驗證的公鑰)加密pre master secret 併發送給server,這裡還要通過填充隨機數,把premaster 擴充到128 位(根據協議)

we should pad these bytes with random data to make the input equal to exactly the size of the modulus (1024 bits/128 bytes).

  • 客戶端通知server client key exchange 傳送金鑰
  • 客戶端通知server Change Cipher Spec,這個是最後一條未加密的訊息

Deriving the Master Secret

現在客戶端(瀏覽器)有了3個隨機值,我們的random 、server 的random 、我們生成的premaster,這三個值雖然都是偽隨機,但是合併在一起,就十分的接近隨機了。通過這3個值,我們就可以獲取到一個 隨機金鑰,用於對稱加密

master_secret = PRF(pre_master_secret, “master secret”, ClientHello.random + ServerHello.random)

到這裡,client 和 server就都有了一個master secret可以用於加密。

Generating Keys

到這裡,我們還需要生成一些key 用於加密的過程。

根據加密方式的不同,可能生成的這些附加key 也不相同。比如,如果是塊加密,那麼我們還需要生成IV,如果是非流式加密就不需要

Prepare to be Encrypted

客戶端傳送finished message,包含一個含有收到的全部握手資訊的校驗值(12位元組)。並且在頭部加入0x14 表示 finish、 0x00 0x00 0x0c 表示12位元組

verify_data = PRF(master_secret, “client finished”, MD5(handshake_messages) + SHA-1(handshake_messages) )

server同樣的方式相應client,並且驗證client。

在這之後握手過程就結束了,可以進入應用層的 加密的 http 服務了。