HTTPS 溫故知新(四) —— 直觀感受 TLS 握手流程(下)

2022-02-03 五分選手
HTTPS 溫故知新(四) —— 直觀感受 TLS 握手流程(下)

在 HTTPS 開篇的文章中,筆者分析了 HTTPS 之所以安全的原因是因為 TLS 協議的存在。TLS 能保證信息安全和完整性的協議是記錄層協議。(記錄層協議在上一篇文章中詳細分析了)。看完上篇文章的讀者可能會感到疑惑,TLS 協議層加密的密鑰是哪裡來的呢?客戶端和服務端究竟是如何協商 Security Parameters 加密參數的?這篇文章就來詳細的分析一下 TLS 1.2 和 TLS 1.3 在 TLS 握手層上的異同點。

TLS 1.3 在 TLS 1.2 的基礎上,針對 TLS 握手協議最大的改進在於提升速度和安全性。本篇文章會重點分析這兩塊。

先簡述一下 TLS 1.3 的一些優化和改進:

減少握手等待時間,將握手時間從 2-RTT 降低到 1-RTT,並且增加 0-RTT 模式。

刪除 RSA 密鑰協商方式,靜態的 Diffie-Hellman 密碼套件也被刪除了。因為 RSA 不支持前向加密性。TLS 1.3 只支持 (EC)DHE 的密鑰協商算法。刪除了 RSA 的方式以後,能有效預防心臟出血[1]的攻擊。「所有基於公鑰的密鑰交換算法現在都能提供前向安全」。TLS 1.3 規範中只支持 5 種密鑰套件,TLS13-AES-256-GCM-SHA384、TLS13-CHACHA20-POLY1305-SHA256、TLS13-AES-128-GCM-SHA256、TLS13-AES-128-CCM-8-SHA256、TLS13-AES-128-CCM-SHA256,隱藏了非對稱加密密鑰協商算法,因為默認都是橢圓曲線密鑰協商。

刪除對稱加密中,分組加密和 MAC 導致的一些隱患。在 TLS1.3 之前的版本中,選擇的是 MAC-then-Encrypt 方式。但是這種方式帶來了一些漏洞,例如 BEAST[2],一系列填充 oracle 漏洞(Lucky 13[3] 和 Lucky Microseconds[4])。CBC 模式和填充之間的交互也是 SSLv3 和一些 TLS 實現中廣泛宣傳的 POODLE[5] 漏洞原因。在 TLS 1.3 中,已移除所有有安全隱患的密碼和密碼模式。你不能再使用 CBC 模式密碼或不安全的流式密碼,如 RC4 。TLS 1.3 中允許的唯一類型的對稱加密是一種稱為 AEAD(authenticated encryption with additional data)的新結構,它將加密性和完整性整合到一個無縫操作中。

在 TLS 1.3中,刪除了 PKCS#1 v1.5 的支持,而選擇更新的設計 RSA-PSS,提高了安全性。認證方面通過非對稱算法,例如,RSA, 橢圓曲線數字籤名算法(ECDSA),或 Edwards 曲線數字籤名算法(EdDSA)完成,或通過一個對稱的預共享密鑰(PSK)。

在 TLS 1.2 的握手流程中,只有 ChangeCipherSpec 之後的消息會被加密,如 Finished 消息和 NewSessionTicket,其他的握手子消息不會加密。TLS 1.3 針對這個問題,對握手中大部分子消息全部進行加密處理。這樣可以有效的預防 FREAK,LogJam 和 CurveSwap 這些降級攻擊(降級攻擊是中間人利用協商,強制使通信雙方使用能被支持的最低強度的加密算法,從而暴力攻擊計算出密鑰,允許攻擊者在握手時偽造 MAC)。在TLS 1.3中,這種類型的降級攻擊是不可能的,因為伺服器現在籤署了整個握手,包括密碼協商。

密鑰導出函數被重新設計,由 TLS 1.2 的 PRF 算法改為更加安全的 HKDF 算法。廢除 Session ID 和 Session Ticket 會話恢復方式,統一通過 PSK 的方式進行會話恢復,並在 NewSessionTicket 消息中添加過期時間和用於混淆時間的偏移值。

更多重要的變更,見筆者之前的文章 《TLS 1.3 Introduction》[6]

七. TLS 1.3 首次握手流程❝

由於筆者在之前的某篇文章中已經將 TLS 1.3 握手流程的細節分析過了,所以這篇文章中不會像上篇分析 TLS 1.2 中那麼詳細,如果想了解 TLS 1.3 中細節,請閱讀這篇文章《TLS 1.3 Handshake Protocol》[7]。本篇文章主要從 wireshark 角度帶讀者直觀感受 TLS 1.3 的握手流程。

在 TLS 1.3 中,存在 4 種密鑰協商的方法:

Client 支持的加密套件列表。密碼套件裡面中能體現出 Client 支持的 AEAD 算法或者 HKDF 哈希對。"supported_groups" 的擴展 和 "key_share" 擴展。"supported_groups" 這個擴展表明了 Client 支持的 (EC)DHE groups,"key_share" 擴展表明了 Client 是否包含了一些或者全部的(EC)DHE共享。"signature_algorithms" 籤名算法和 "signature_algorithms_cert" 籤名證書算法的擴展。"signature_algorithms" 這個擴展展示了 Client 可以支持了籤名算法有哪些。"signature_algorithms_cert" 這個擴展展示了具體證書的籤名算法。"pre_shared_key" 預共享密鑰和 "psk_key_exchange_modes" 擴展。預共享密鑰擴展包含了 Client 可以識別的對稱密鑰標識。"psk_key_exchange_modes" 擴展表明了可能可以和 psk 一起使用的密鑰交換模式。

第一種方法是 TLS 1.2 中已經存在的,通過 ClientHello 中的 Cipher Suites 進行協商。第二種方法是 TLS 1.3 新增的,在 TLS 1.3 中完整握手就是通過這種方法實現的。第三種方法也是 TLS 1.3 新增的。這種方法沒有第二種方法用的多。第四種方法也是 TLS 1.3 新增的,它將 TLS 1.2 中 Session ID 和 Session Ticket 廢除以後,統一通過 PSK 的方式進行會話恢復。TLS 1.3 中的 0-RTT 模式也是通過 PSK 進行的。

TLS 1.3 完整握手的流程如下:

          Client                                               Server

          ClientHello
          + key_share               --->
                                                          ServerHello
                                                          + key_share
                                                {EncryptedExtensions}
                                                {CertificateRequest*}
                                                       {Certificate*}
                                                 {CertificateVerify*}
                                                           {Finished}
                                    <---     [Application Data*]
          {Certificate*}
          {CertificateVerify*}
          {Finished}                --->
                                    <---      [NewSessionTicket]
          [Application Data]        <-->      [Application Data]

在 TLS 1.3 握手中,主要能分為 3 個階段:

密鑰交換:建立共享密鑰數據並選擇密碼參數。在這個階段之後所有的數據都會被加密。Server 參數:建立其它的握手參數(Client 是否被認證,應用層協議支持等)。認證:認證 Server(並且選擇性認證 Client),提供密鑰確認和握手完整性。

密鑰交換是 ClientHello 和 ServerHello,Server 參數是 EncryptedExtensions 和 CertificateRequest 消息。認證是 Certificate、CertificateVerify、Finished。

Client 發起完整握手流程從 ClientHello 開始:

      uint16 ProtocolVersion;
      opaque Random[32];

      uint8 CipherSuite[2];    /* Cryptographic suite selector */

      struct {
          ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
          Random random;
          opaque legacy_session_id<0..32>;
          CipherSuite cipher_suites<2..2^16-2>;
          opaque legacy_compression_methods<1..2^8-1>;
          Extension extensions<8..2^16-1>;
      } ClientHello;

在 ClientHello 結構體重,legacy_version = 0x0303,0x0303 是 TLS 1.2 的版本號,這個欄位規定必須設置成這個值。其他欄位和 TLS 1.2 含義相同,不再贅述了。

在 TLS 1.3 的 ClientHello 的 Extension 中,一定會有 supported_versions 這個欄位,如果這個欄位,ClientHello 會被解讀成 TLS 1.2 的 ClientHello 消息。在 TLS 1.3 中 Server 根據 supported_versions 這個欄位來決定是否協商 TLS 1.3 。

TLS 1.3 之所以能比 TLS 1.2 完整握手減少 1-RTT 的原因就在 ClientHello 中就已經包含了 (EC)DHE 所需要的密鑰參數,不需要像 TLS 1.2 中額外用第二次 RTT 來進行 DH 協商參數。在 TLS 1.3 的 ClientHello 的 Extension 中,帶有 key_share 擴展,這個擴展中包含了 Client 所能支持的 (EC)DHE 算法的密鑰參數。並且 Extension 中還會有 supported_groups 擴展,這個擴展表明了 Client 支持的用於密鑰交換的命名組。按照優先級從高到低。

Server 收到 ClientHello 以後,回應一條 ServerHello 消息:

      struct {
          ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
          Random random;
          opaque legacy_session_id_echo<0..32>;
          CipherSuite cipher_suite;
          uint8 legacy_compression_method = 0;
          Extension extensions<6..2^16-1>;
      } ServerHello;

在 ServerHello 消息中,legacy_version = 0x0303,這個也是 TLS 1.3 規範的規定,這個值必須固定填 0x0303(TLS 1.2)。Server 會讀取 ClientHello 擴展中 "supported_versions" 擴展欄位,如果 Client 能支持 TLS 1.3,那麼 Server 在 ServerHello 擴展中的 "supported_versions" 擴展欄位標識可以進行 TLS 1.3 的握手。

Server 在協商 TLS 1.3 之前的版本,必須要設置 ServerHello.version,不能發送 "supported_versions" 擴展。Server 在協商 TLS 1.3 版本時候,必須發送 "supported_versions" 擴展作為響應,並且擴展中要包含選擇的 TLS 1.3 版本號(0x0304)。還要設置 ServerHello.legacy_version 為 0x0303(TLS 1.2)。Client 必須在處理 ServerHello 之前檢查此擴展(儘管需要先解析 ServerHello 以便讀取擴展名)。如果 "supported_versions" 擴展存在,Client 必須忽略 ServerHello.legacy_version 的值,只使用 "supported_versions" 中的值確定選擇的版本。如果 ServerHello 中的 "supported_versions" 擴展包含了 Client 沒有提供的版本,或者是包含了 TLS 1.3 之前的版本(本來是協商 TLS 1.3 的,卻又包含了 TLS 1.3 之前的版本),Client 必須立即發送 "illegal_parameter" alert 消息中止握手。

在 ServerHello 的 Extension 中必須要有的這 2 個擴展,supported_versions、key_share(如果是 PSK 會話恢復方式,還必須包含 pre_shared_key)。key_share 擴展標識了 Server 選擇了 Client 支持的哪一個橢圓曲線,以及它對應的密鑰協商所需參數。這裡有兩種情況,一種是協商 Diffie-Hellman 參數,具體分析見這一章節[8],另外一種協商是 ECDHE 參數,具體分析見這一章節[9]。

key_share 傳輸過程中並沒有使用私鑰加密,整個過程的不可抵賴和防篡改是通過 CertificateVerify 驗證 Server 持有私鑰,以及 Finished 消息使用 HMAC 驗證歷史消息來確定的。

發完 ServerHello 消息以後,Server 會繼續發送 EncryptedExtensions 和 CertificateRequest 消息,如果對 Client 不進行認證,就不需要發送 CertificateRequest 消息。上面這 2 條消息都是加密的,通過 server_handshake_traffic_secret 中派生的密鑰加密的。

early secret 和 ecdhe secret 導出 server_handshake_traffic_secret。再從 server_handshake_traffic_secret中導出 key 和 iv,使用該 key 和 iv 對 Server hello 之後的握手消息加密。同樣的計算 client_handshake_traffic_secret,使用對應的 key 和 iv 進行解密後續的握手消息。

       Early Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(0, PSK) = HKDF-Extract(0, 0)
       Handshake Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(Derive-Secret(Early Secret, "derived", ""), (EC)DHE)

       client_handshake_traffic_secret = Derive-Secret(Handshake Secret, "c hs traffic", ClientHello...ServerHello)
       server_handshake_traffic_secret = Derive-Secret(Handshake Secret, "s hs traffic", ClientHello...ServerHello)

       client_write_key = HKDF-Expand-Label(client_handshake_traffic_secret, "key", "", key_length)
       client_write_iv  = HKDF-Expand-Label(client_handshake_traffic_secret, "iv", "", iv_length)

       server_write_key = HKDF-Expand-Label(server_handshake_traffic_secret, "key", "", key_length)
       server_write_iv  = HKDF-Expand-Label(server_handshake_traffic_secret, "iv", "", iv_length)

EncryptedExtensions 消息包含應該被保護的擴展。即,任何不需要建立加密上下文但不與各個證書相互關聯的擴展。比如 ALPN 擴展。Client 必須檢查 EncryptedExtensions 消息中是否存在任何禁止的擴展,如果有發現禁止的擴展,必須立即用 "illegal_parameter" alert 消息中止握手。

   Structure of this message:

      struct {
          Extension extensions<0..2^16-1>;
      } EncryptedExtensions;

CertificateRequest 消息細節,見這一章節[10]

接下來 Server 還要繼續發送 Certificate、CertificateVerify、Finished 消息。這 3 條消息是握手消息的最後 3 條消息。這 3 條消息使用從 sender_handshake_traffic_secret 派生出來的密鑰進行加密。

Server 發送自己的 Certificate 給 Client,在 Certificate 消息中,有 4 種情況,第一種包含了 OCSP Status and SCT Extensions,細節請看這一章節[11],第二種包含了 Server Certificate Selection,細節請看這一章節[12],第三種包含了 Client Certificate Selection,細節請看這一章節[13],最後一種包含了 Receiving a Certificate Message,細節請看這一章節[14]。

Server 發送完 Certificate 消息以後,緊接著是 CertificateVerify 消息。Server 將當前所有的握手消息進行籤名,具體驗證過程見這一章節[15]。

最後一條消息是 Finished 消息。它對提供握手和計算密鑰的身份驗證起了至關重要的作用。

用於計算 Finished 消息的密鑰是使用 HKDF,特別的:

   finished_key =
       HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)

BaseKey 是 handshake_traffic_secret。

這條消息的數據結構是:

      struct {
          opaque verify_data[Hash.length];
      } Finished;

verify_data 按照如下方法計算:

      verify_data =
          HMAC(finished_key,
               Transcript-Hash(Handshake Context,
                               Certificate*, CertificateVerify*))

      * Only included if present.

HMAC [RFC2104][16] 使用哈希算法進行握手。如上所述,HMAC 輸入通常是通過動態的哈希實現的,即,此時僅是握手的哈希。

在以前版本的 TLS 中,verify_data 的長度總是 12 個八位字節。在 TLS 1.3 中,它是用來表示握手的哈希的 HMAC 輸出的大小。

「注意:警報和任何其他非握手記錄類型不是握手消息,並且不包含在哈希計算中」

Finished 消息之後的任何記錄都必須在適當的應用程式流量密鑰下加密。特別是,這包括 Server 為了響應 Client 的 Certificate 消息和 CertificateVerify 消息而發送的任何 alert。

Finish 消息發送完後,再導出最終對稱加密的密鑰。從 Handshake Secret 中導出 master secret,再從 master secret 導出兩個方向的對稱密鑰 key 和 iv。

       Master Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(Derive-Secret(Handshake Secret, "derived", ""), 0)
       client_application_traffic_secret_0 = Derive-Secret(Master Secret, "c ap traffic", ClientHello...server Finished)
       server_application_traffic_secret_0 = Derive-Secret(Master Secret, "s ap traffic", ClientHello...server Finished)

Finished 消息發送以後,在完整握手的流程中,Server 收到 Client 的 Finished 消息後,驗證完後,還需要發送 NewSessionTicket 消息。通過 master secret 和整個握手的摘要,計算最後的 resumption secret。

NewSessionTicket 使用 server_application_traffic_secret 加密。在加密的 ticket過程中,TLS 1.3 相比 TLS 1.2,還包含了當前的創建時間,因此可以方便的配置和驗證 ticket 的過期時間。

注意:雖然恢復主密鑰取決於 Client 的第二次 flight,但是不請求 Client 身份驗證的 Server 可以獨立計算轉錄哈希的剩餘部分,然後在發送 Finished 消息後立即發送 NewSessionTicket 而不是等待 Client 的 Finished 消息。這可能適用於 Client 需要並行打開多個 TLS 連接並且可以從減少恢復握手的開銷中受益的情況。

      struct {
          uint32 ticket_lifetime;
          uint32 ticket_age_add;
          opaque ticket_nonce<0..255>;
          opaque ticket<1..2^16-1>;
          Extension extensions<0..2^16-2>;
      } NewSessionTicket;

ticket_lifetime:
這個欄位表示 ticket 的生存時間,這個時間是以 ticket 發布時間為網絡字節順序的 32 位無符號整數表示以秒為單位的時間。Server 禁止使用任何大於 604800 秒(7 天)的值。值為零表示應立即丟棄 ticket。無論 ticket_lifetime 如何,Client 都不得緩存超過 7 天的 ticket,並且可以根據本地策略提前刪除 ticket。Server 可以將 ticket 視為有效的時間段短於 ticket_lifetime 中所述的時間段。「這是 TLS 1.2 和 TLS 1.3 的區別,TLS 1.2 中並不包含 ticket 有效時間段(即生存時間)」

ticket_age_add:
安全的生成的隨機 32 位值,用於模糊 Client 在 "pre_shared_key" 擴展中包含的 ticket 的時間。Client 的 ticket age 以模 2 ^ 32 的形式添加此值,以計算出 Client 要傳輸的值。Server 必須為它發出的每個 ticket 生成一個新值。

ticket_nonce:
每一個 ticket 的值,在本次連接中發出的所有的 ticket 中是唯一的。

ticket:
這個值是被用作 PSK 標識的值。ticket 本身是一個不透明的標籤。它可以是資料庫查找鍵,也可以是自加密和自我驗證的值。

extensions:
ticket 的一組擴展值。Client 必須忽略無法識別的擴展。

當前為 NewSessionTicket 定義的唯一擴展名是 "early_data",表示該 ticket 可用於發送 0-RTT 數據。它包含以下值:

max_early_data_size:
這個欄位表示使用 ticket 時允許 Client 發送的最大 0-RTT 數據量(以字節為單位)。數據量僅計算應用數據有效載荷(即,明文但不填充或內部內容類型字節)。Server 如果接收的數據大小超過了 max_early_data_size 字節的 0-RTT 數據,應該立即使用 "unexpected_message" alert 消息終止連接。請注意,由於缺少加密材料而拒絕 early data 的 Server 將無法區分內容中的填充部分,因此 Client 不應該依賴於能夠在 early data 記錄中發送大量填充內容。

PSK 關聯的 ticket 計算方法如下:

       HKDF-Expand-Label(resumption_master_secret,
                        "resumption", ticket_nonce, Hash.length)

因為 ticket_nonce 值對於每個 NewSessionTicket 消息都是不同的,所以每個 ticket 會派生出不同的 PSK。

請注意,原則上可以繼續發布新 ticket,該 ticket 無限期地延長生命周期,這個生命周期是最初從初始非 PSK 握手中(最可能與對等證書相關聯)派生得到的密鑰材料的生命周期。

建議實現方對密鑰材料這些加上總壽命時間的限制。這些限制應考慮到對等方證書的生命周期,幹預撤銷的可能性以及自從對等方在線 CertificateVerify 籤名到當前時間的這段時間。

完整握手的流程圖如下:

握手完成以後,還可能受到 KeyUpdate 的子消息。這個子消息是負責更新密鑰以保證 AEAD 安全性的 Key Update(KU) 消息。

TLS 協議的最終目的是協商出會話過程使用的對稱密鑰和加密算法,雙方最終使用該密鑰和對稱加密算法對消息進行加密。AEAD(Authenticated_Encrypted_with_associated_data)是 TLS 1.3 中唯一保留和支持的加密方式。AEAD 將完整性校驗和數據加密兩種功能集成在同一算法中完成。TLS 1.2 還支持流加密和 CBC 分組模式的塊加密方法,使用 MAC 來進行完整性校驗數據,這兩種方式均被證明有一定的安全缺陷。

但是即使是 AEAD 仍然有研究表明[17]它有一定局限性:使用同一密鑰加密的明文達到一定長度後,就不能再保證密文的安全性。因此,TLS 1.3 中引入了密鑰更新機制,一方可以(通常是伺服器)向另一方發送 Key Update(KU)消息,對方收到消息後對當前會話密鑰再使用一次 HKDF,計算出新的會話密鑰,使用該密鑰完成後續的通信。

如果想了解更多關於 Key Update 消息的,可以看筆者之前的這篇文章 《Key and Initialization Vector Update》[18]

❞八. 直觀感受 TLS 1.3 首次握手流程

這一章,筆者用 wireshark 抓取 TLS 1.3 握手流程中的數據包,讓讀者直觀感受一下 TLS 1.3 握手流程。

上圖是 TLS 1.3 中的 ClientHello 消息。在這個消息的結構中,與 TLS 1.2 差別主要在擴展上。TLS 1.2 中有的擴展,TLS 1.3 也有,但是 TLS 1.3 中多了一些重要的擴展。

上圖是 TLS 1.3 中 ClientHello 首次完整握手中所有的擴展。

展開這些擴展,可以看到,TLS 1.2 中有的擴展,TLS 1.3 都包含。並且數據結構也都沒有發生變化。

這是 TLS 1.3 新增的 key_share 擴展。在這個擴展中,包含了 Client 所能支持的橢圓曲線類型和對應的 (EC)DHE 密鑰協商參數。

psk_key_exchange_modes 也是 TLS 1.3 中新增的擴展,這個擴展語意是 Client 僅支持使用具有這些模式的 PSK。這就限制了在這個 ClientHello 中提供的 PSK 的使用,也限制了 Server 通過 NewSessionTicket 提供的 PSK 的使用。

psk_ke: 代表僅 PSK 密鑰建立。在這種模式下,Server 不能提供 "key_share" 值。

psk_dhe_ke: PSK 和 (EC)DHE 建立。在這種模式下,Client 和 Server 必須提供 "key_share" 值。

supported_versions 是 TLS 1.3 中必帶的擴展,如果沒有這個擴展,Server 會認為 Client 只能支持 TLS 1.2,於是接下來的握手會進行 TLS 1.2 的握手流程。

在 ServerHello 中回應 Client,supported_versions 擴展中包含了協商以後的協議版本。

在 ServerHello 中也會帶上 Server 的密鑰協商參數,放在 key_share 擴展中。

EncryptedExtensions 子消息中會帶和任何不需要建立加密上下文但不與各個證書相互關聯的擴展,比如這裡的 server_name 和 ALPN 擴展。

Certificate 消息中會帶上 OCSP Response 擴展。

ChangeCipherSpec 和 Finished 消息與 TLS 1.2 中沒有區別。

首次完整握手完成以後,還會發送 NewSessionTicket 消息。在這個消息中會帶 early_data 的擴展。如果有這個擴展,就表明 Server 可以支持 0-RTT。如果沒有帶這個擴展,如下圖:

如果沒有帶這個擴展,表明 Server 不支持 0-RTT,Client 在下次會話恢復的時候不要發送 early_data 擴展。

九. TLS 1.3 會話恢復

這裡網上很多文章對 TLS 1.3 第二次握手有誤解。經過自己實踐以後發現了「真理」。

TLS 1.3 在宣傳的時候就以 0-RTT 為主,大家都會認為 TLS 1.3 再第二次握手的時候都是 0-RTT 的,包括網上一些分析的文章裡面提到的最新的 PSK 密鑰協商,PSK 密鑰協商並非是 0-RTT 的。

TLS 1.3 再次握手其實是分兩種:會話恢復模式、0-RTT 模式。非 0-RTT 的會話恢復模式和 TLS 1.2 在耗時上沒有提升,都是 1-RTT,只不過比 TLS 1.2 更加安全了。只有在 0-RTT 的會話恢復模式下,TLS 1.3 才比 TLS 1.2 有提升。具體提升對比見下表:


HTTP/2 + TLS 1.2 首次連接HTTP/2 + TLS 1.2 會話恢復HTTP/2 + TLS 1.3 首次連接HTTP/2 + TLS 1.3 會話恢復HTTP/2 + TLS 1.3 0-RTTDNS 解析1-RTT0-RTT1-RTT0-RTT0-RTTTCP 握手1-RTT1-RTT1-RTT1-RTT1-RTTTLS 握手2-RTT1-RTT1-RTT1-RTT0-RTTHTTP 請求1-RTT1-RTT1-RTT1-RTT1-RTT總計5-RTT3-RTT4-RTT3-RTT2-RTT

如果開啟 TCP 的 TFO,收到第一個 HTTPS 響應包的時間,可以在上表的基礎上再減少一個 RTT。

在完整握手中,Client 在收到 Finished 消息以後,還會收到 NewSessionTicket 消息。

      struct {
          uint32 ticket_lifetime;
          uint32 ticket_age_add;
          opaque ticket_nonce<0..255>;
          opaque ticket<1..2^16-1>;
          Extension extensions<0..2^16-2>;
      } NewSessionTicket;

Server 將 ticket_nonce 和發送 Finished 子消息後計算的 resumption_master_secret 一起作為 HKDF-Expand-Label 的入參,計算 NewSessionTicket 中的 ticket 欄位:

     PskIdentity.identity = ticket 
            = HKDF-Expand-Label(resumption_master_secret, "resumption", ticket_nonce, Hash.length)

「TLS 1.2 和 TLS 1.3 的區別,TLS 1.2 中 NewSessionTicket 是主密鑰,而 TLS 1.3 中 ticket 只是一個 PSK」。Client 收到 NewSessionTicket 以後就可以生成 PskIdentity 了,如果有多個 PskIdentity,就都放在 identities 數組中。binders 數組中是與 identities 順序一一對應的 HMAC 值 PskBinderEntry。

      struct {
          opaque identity<1..2^16-1>;
          uint32 obfuscated_ticket_age;
      } PskIdentity;

      opaque PskBinderEntry<32..255>;

      struct {
          PskIdentity identities<7..2^16-1>;
          PskBinderEntry binders<33..2^16-1>;
      } OfferedPsks;

      struct {
          select (Handshake.msg_type) {
              case client_hello: OfferedPsks;
              case server_hello: uint16 selected_identity;
          };
      } PreSharedKeyExtension;

PskBinderEntry 的計算方法:

 PskBinderEntry = HMAC(binder_key, Transcript-Hash(Truncate(ClientHello1)))
       = HMAC(Derive-Secret(HKDF-Extract(0, PSK), "ext binder" | "res binder", ""), Transcript-Hash(Truncate(ClientHello1)))
       
其中     binder_key = Derive-Secret(HKDF-Extract(0, PSK), "ext binder" | "res binder", "")       

HMAC 會包含 PreSharedKeyExtension.identities 欄位。也就是說,HMAC 包含所有的 ClientHello,但是不包含 binder list(否則就出現雞生蛋,蛋生雞的死循環問題了)。Truncate() 函數的作用是把 ClientHello 中的 binders list 移除。

Client 可以把 PSK 保存到本地 cache 中,serverName 作為 cache 的 key。

1. 會話恢復模式

TLS 1.3 中更改了會話恢復機制,廢除了原有的 Session ID 和 Session TIcket 的方式,使用 PSK 的機制,同時 New Session Ticket 中添加了過期時間。TLS 1.2 中 的 ticket 不包含過期時間,可以通過 ticket key 的更新讓之前所有發送的 ticket 都失效,或者在生成 ticket 的時候加入自定義可以判斷過期時間的策略。

在經歷了一次完整握手以後,生成了 PSK,下次握手就會進入會話恢復模式,在 Client hello 中,先在本地 cache 中查找 servername 對應的 PSK,找到後在 Client hello 的 pre_shared_key 擴展中帶上兩部分

Identity: NewSessionTicket 中加密的 ticketBinder: 由 PSK 導出 binder_key,使用 binder_key 對不包含 binder list 部分的 ClientHello 作 HMAC 計算。
       Early Secret = HKDF-Extract(0, PSK)
       binder_key = Derive-Secret(Early Secret, "ext binder" | "res binder", "")
       client_early_traffic_secret = Derive-Secret(Early Secret, "c e traffic", ClientHello)
       early_exporter_master_secret = Derive-Secret(Early Secret, "e exp master", ClientHello)

注意:當存在多種不同類型的擴展的時候,除了 "pre_shared_key" 必須是 ClientHello 的最後一個擴展,其他的擴展間的順序可以是任意的。("pre_shared_key" 可以出現在 ServerHello 中擴展塊中的任何位置)。不能存在多個同一個類型的擴展。

通過 resumption secret 導出 PSK。PSK 會最終導出 earlyData 的加密密鑰,以及 pre_shared_key 擴展中 binder 的 HMAC 密鑰。發送 ClientHello 後,使用 resumption secret 導出的 PskIdentity.identity 生成 PSK,進而導出 client_early_traffic_secret 密鑰,再生成 Key 和 IV,對 early data 加密後發送。

Server 收到帶有 PSK 的 ClientHello 以後,生成協商之後的 keyshare,並檢查 Client hello 中的 pre_shared_key 擴展,解密 PskIdentity.identity(即 ticket),查看該 ticket 是否過期,各項檢查通過以後,由 PSK 導出 binder_key 並計算 client hello 的 HMAC,檢查 binder 是否正確。驗證完 ticket 和 binder 之後,在 ServerHello 擴展中帶上 pre_shared_key 擴展,標識使用哪個 PSK 進行會話恢復。和 Client 一樣,從 resumtion secret 開始導出 PSK,最終導出 earlyData 使用的密鑰。後續的密鑰導出規則和完整握手是一樣的,唯一的區別就是會話恢復多了 PSK,它是作為 early secret 的輸入密鑰材料 IKM。

TLS 1.3 和 TLS 1.2 在會話恢復的密鑰導出上有很大不同,TLS 1.2 會話恢復會直接使用之前的 master secret,然後生成會話密鑰(密鑰塊)。TLS 1.3 只會利用 resumption secret 導出 early data 密鑰的輸入密鑰材料 IKM —— PSK,之後的密鑰導出規則和 TLS 1.3 完整握手是一樣的。

發送完 ServerHello 以後,還需要繼續發送 EncryptedExtensions 和 Finished 消息。不過會話恢復模式就不需要再發送 Certificate 和 CerficateVerify 消息了。只要證明了雙方都持有相同的 PSK,就不再需要證書認證來證明雙方的身份,這樣看來,PSK 也算是一種身份認證機制。

流程圖如下:

          Client                                               Server

   Initial Handshake:
          ClientHello
          + key_share               --->
                                                          ServerHello
                                                          + key_share
                                                {EncryptedExtensions}
                                                {CertificateRequest*}
                                                       {Certificate*}
                                                 {CertificateVerify*}
                                                           {Finished}
                                    <---     [Application Data*]
          {Certificate*}
          {CertificateVerify*}
          {Finished}                --->
                                    <---      [NewSessionTicket]
          [Application Data]        <-->      [Application Data]


   Subsequent Handshake:
          ClientHello
          + key_share*
          + pre_shared_key          --->
                                                          ServerHello
                                                     + pre_shared_key
                                                         + key_share*
                                                {EncryptedExtensions}
                                                           {Finished}
                                    <---     [Application Data*]
          {Finished}                --->
          [Application Data]        <-->      [Application Data]

筆者之前寫過一篇關於 PSK 細節分析的文章,如果讀者感興趣,可以看《Pre-Shared Key Extension》[19]。這裡簡單描述一下 PSK 擴展。

"pre_shared_key" 擴展用來協商標識的,這個標識是與 PSK 密鑰相關聯的給定握手所使用的預共享密鑰的標識。

這個擴展中的 "extension_data" 欄位包含一個 PreSharedKeyExtension 值:

      struct {
          opaque identity<1..2^16-1>;
          uint32 obfuscated_ticket_age;
      } PskIdentity;

      opaque PskBinderEntry<32..255>;

      struct {
          PskIdentity identities<7..2^16-1>;
          PskBinderEntry binders<33..2^16-1>;
      } OfferedPsks;

      struct {
          select (Handshake.msg_type) {
              case client_hello: OfferedPsks;
              case server_hello: uint16 selected_identity;
          };
      } PreSharedKeyExtension;

identity:
key 的標籤。例如,一個 ticket 或者是一個外部建立的預共享密鑰的標籤。

obfuscated_ticket_age:
age of the key 的混淆版本。這一章節[20]描述了通過 NewSessionTicket 消息建立,如何為標識(identities)生成這個值。對於外部建立的標識(identities),應該使用 0 的 obfuscated_ticket_age,並且 Server 也必須忽略這個值。

identities:
Client 願意和 Server 協商的 identities 列表。如果和 "early_data" 一起發送,第一個標識被用來標識 0-RTT 的。

binders:
一系列的 HMAC 值。和 identities 列表中的每一個值都一一對應,並且順序一致。

selected_identity:
Server 選擇的標識,這個標識是以 Client 列表中標識表示為基於 0 的索引。

每一個 PSK 都和單個哈希算法相關聯。對於通過 ticket 建立的 PSK,當 ticket 在連接中被建立,這時候用的哈希算法是 KDF 哈希算法。對於外部建立的 PSK,當 PSK 建立的時候,哈希算法必須設置,如果沒有設置,默認算法是 SHA-256。Server 必須確保它選擇的是兼容的 PSK (如果有的話) 和密鑰套件。

在接受PSK密鑰建立之前,Server 必須先驗證相應的 binder 值。如果這個值不存在或者未驗證,則 Server 必須立即中止握手。Server 不應該嘗試去驗證多個 binder,而應該選擇單個 PSK 並且僅驗證對應於該 PSK 的 binder。為了接受 PSK 密鑰建立連接,Server 發送 "pre_shared_key" 擴展,標明它所選擇的 identity。

Client 必須驗證 Server 的 selected_identity 是否在 Client 提供的範圍之內。Server 選擇的加密套件標明了與 PSK 關聯的哈希算法,如果 ClientHello "psk_key_exchange_modes" 有需要,Server 還應該發送 "key_share" 擴展。如果這些值不一致,Client 必須立即用 "illegal_parameter" alert 消息中止握手。

如果 Server 提供了 "early_data" 擴展,Client 必須驗證 Server 的 selected_identity 是否為 0。如果返回任何其他值,Client 必須使用 "illegal_parameter" alert 消息中止握手。

"pre_shared_key" 擴展必須是 ClientHello 中的最後一個擴展(這有利於下面的描述的實現)。Server 必須檢查它是最後一個擴展,否則用 "illegal_parameter" alert 消息中止握手。

(1) Ticket Age

從 Client 的角度來看,ticket 的時間指的是,收到 NewSessionTicket 消息開始到當前時刻的這段時間。Client 決不能使用時間大於 ticket 自己標明的 "ticket_lifetime" 這個時間的 ticket。每個 PskIdentity 中的 "obfuscated_ticket_age" 欄位都必須包含 ticket 時間的混淆版本,混淆方法是用 ticket 時間(毫秒為單位)加上 "ticket_age_add" 欄位,最後對 2^32 取模。除非這個 ticket 被重用了,否則這個混淆就可以防止一些相關聯連接的被動觀察者。注意,NewSessionTicket 消息中的 "ticket_lifetime"  欄位是秒為單位,但是 "obfuscated_ticket_age" 是毫秒為單位。因為 ticke lifetime 限制為一周,32 位就足夠去表示任何合理的時間,即使是以毫秒為單位也可以表示。

(2) PSK Binder

PSK binder 的值形成了 2 種綁定關係,一種是 PSK 和當前握手的綁定,另外一種是 PSK 產生以後(如果是通過 NewSessionTicket 消息)的握手和當前握手的綁定。每一個在 binder 列表中的條目都會根據有一部分 ClientHello 的哈希副本計算 HMAC,最終 HMAC 會包含 PreSharedKeyExtension.identities 欄位。也就是說,HMAC 包含所有的 ClientHello,但是不包含 binder list 。如果存在正確長度的 binders,消息的長度欄位(包括總長度,擴展塊的長度和 "pre_shared_key" 擴展的長度)都被設置。

PskBinderEntry 的計算方法和 Finished 消息一樣。但是 BaseKey 是派生的 binder_key,派生方式是通過提供的相應的 PSK 的密鑰派生出來的。

如果握手包括 HelloRetryRequest 消息,則初始的 ClientHello 和 HelloRetryRequest 隨著新的 ClientHello 一起被包含在副本中。例如,如果 Client 發送 ClientHello,則其 binder 將通過以下方式計算:

      Transcript-Hash(Truncate(ClientHello1))

Truncate() 函數的作用是把 ClientHello 中的 binders list 移除。

如果 Server 響應了 HelloRetryRequest,那麼 Client 會發送 ClientHello2,它的 binder 會通過以下方式計算:

      Transcript-Hash(ClientHello1,
                      HelloRetryRequest,
                      Truncate(ClientHello2))

完整的 ClientHello1/ClientHello2 都會包含在其他的握手哈希計算中。請注意,在第一次發送中,Truncate(ClientHello1) 是直接計算哈希的,但是在第二次發送中,ClientHello1 計算哈希,並且還會再注入一條 "message_hash" 消息。

關於會話恢復密鑰的一些計算流程表示出來如下:

             0
             |
             v
   PSK ->  HKDF-Extract = Early Secret
             |
             +> Derive-Secret(., "ext binder" | "res binder", "")
             |                     = binder_key
             |
             +> Derive-Secret(., "c e traffic", ClientHello)
             |                     = client_early_traffic_secret
             |
             +> Derive-Secret(., "e exp master", ClientHello)
             |                     = early_exporter_master_secret
             v
       Derive-Secret(., "derived", "")
             |
             v

PSK 會話恢復的流程圖如下:

2. 0-RTT 模式

先來看看 0-RTT 在整個草案裡面的變更歷史。

草案變更draft-070-RTT 最早是在 draft-07 中加入了基礎的支持draft-111. 在 draft-11 中刪除了early_handshake內容類型
2. 使用一個 alert 終止 0-RTT 數據draft-131. 刪除 0-RTT 客戶端身份驗證
2. 刪除 (EC)DHE 0-RTT
3. 充實 0-RTT PSK 模式並 shrink EarlyDataIndicationdraft-141. 移除了 0-RTT EncryptedExtensions
2. 降低使用 0-RTT 的門檻
3. 闡明 0-RTT 向後兼容性
4. 說明 0-RTT 和 PSK 密鑰協商的相互關係draft-15討論 0-RTT 時間窗口draft-161. 禁止使用 0-RTT 和 PSK 的 CertificateRequest
2. 放寬要求檢查 SNI 的 0-RTTdraft-171. 刪除 0-RTT Finished 和 resumption_context,並替換為 PSK 本身的 psk_binder 欄位
2. 協調密碼套件匹配的要求:會話恢復只需要匹配 KDF 但是對於 0-RTT 需要匹配整個密碼套件。允許 PSK 實際去協商密碼套件
3. 闡明允許使用 PSK 進行 0-RTT 的條件draft-21關於 0-RTT 和重放的討論,建議實現一些反重放機制

從歷史來看,人們從功能問題討論到性能問題,最後討論到安全問題。

據 Google 統計,全網有 60% 的網站訪問流量是來自於新訪問的網站和過去曾經訪問過但是隔了一段時間再次訪問。這部分流量在 TLS 1.3 的優化下,已經從 2-RTT 降低到 1-RTT 了。剩下 40% 的網站訪問流量是來自於會話恢復,TLS 1.3 廢除了之前的 Session ID 和 Session Ticket 的會話恢復的方式,統一成了 PSK 方式,使得原有會話恢復變的更加安全。但是 TLS 1.3 的會話恢復並沒有降低 RTT,依舊停留在了 1-RTT。為了進一步降低延遲,於是提出了 0-RTT 的概念。0-RTT 能讓用戶有更快更順滑更好的用戶體驗,在行動網路上更加明顯。

TLS 1.3 的裡程碑標誌就是添加了 0-RTT 會話恢復模式。也就是說,當 client 和 server 共享一個 PSK(從外部獲得或通過一個以前的握手獲得)時,TLS 1.3 允許 client 在第一個發送出去的消息中攜帶數據("early data")。Client 使用這個 PSK 生成 client_early_traffic_secret 並用它加密 early data。Server 收到這個 ClientHello 之後,用 ClientHello 擴展中的 PSK 導出 client_early_traffic_secret 並用它解密 early data。

0-RTT 會話恢復模式如下:

         Client                                               Server

         ClientHello
         + early_data
         + key_share*
         + psk_key_exchange_modes
         + pre_shared_key
         (Application Data*)     --->
                                                         ServerHello
                                                    + pre_shared_key
                                                        + key_share*
                                               {EncryptedExtensions}
                                                       + early_data*
                                                          {Finished}
                                 <---       [Application Data*]
         (EndOfEarlyData)
         {Finished}              --->
         [Application Data]      <-->        [Application Data]

想實現 0-RTT 也是有一些條件的,條件比較苛刻,如果條件有一條不滿足,會話恢復都只能是 1-RTT 的 PSK 會話恢復模式。

0-RTT 的「開啟條件」是:

Server 在前一次完整握手中,發送了 NewSessionTicket,並且 Session ticket 中存在max_early_data_size 擴展表示願意接受 early data。如果沒有這個擴展,0-RTT 無法開啟。在 PSK 會話恢復的過程中,ClientHello 的擴展中配置了 early data 擴展,表示 Client 想要開啟 0-RTT 模式。Server 在 Encrypted Extensions 消息中攜帶了 early data 擴展表示同意讀取 early data。0-RTT 模式開啟成功。

只有同時滿足了上面 3 個條件,才能開啟 0-RTT 會話恢復模式。否則握手會是 1-RTT 的會話恢復模式。

目前不少瀏覽器雖然支持 TLS 1.3 協議,但是還不支持發送 early data,所以它們也沒法啟用 0-RTT 模式的會話恢復。

從 0-RTT 的開啟條件中就能看出它和上面 1-RTT 會話恢復的區別。ClientHello 中需要帶 early_data 的擴展,Server 要在 Encrypted Extensions 消息中攜帶了 early_data 擴展,Client 發送完 early_data 數據以後,還需要回一個 EndOfEarlyData 的子消息。

      struct {} EndOfEarlyData;

Client 在發送 early_data 之後,可以一直發 early_data 數據。如果 Server 在 EncryptedExtensions 中發送了 "early_data" 擴展,則 Client 必須在收到 Server 的 Finished 消息後發送 EndOfEarlyData 消息。如果 Server 沒有在 EncryptedExtensions中發送 "early_data" 擴展,那麼 Client 禁止發送 EndOfEarlyData 子消息。此消息表示已傳輸完了所有 0-RTT application_data消息(如果有),並且接下來的記錄受到握手流量密鑰的保護。Server 不能發送此消息,Client 如果收到了這條消息,那麼必須使用 "unexpected_message" alert 消息終止連接。這條消息使用從 client_early_traffic_secret 中派生出來的密鑰進行加密保護。

「注意」: early data 並不參與最後的 Finished 校驗計算,其次,EndOfEarlyData 子消息也不參與最後 application traffic secret 的計算。

Server 在接收到 ClientHello 以後,應立即發送 ServerHello、ChangeCipherSpec、EncryptedExtensions、Finished 子消息。

Server 想拒絕 Client 的 0-RTT 會話恢復,只要打破 3 個開啟條件即可:

拒絕 PSK。Server 在 ServerHello 中不加入 pre_shared_key 擴展,那麼握手就會回退到完整握手,自然拒絕了 0-RTT。只拒絕 early_data,接受 PSK。在 ServerHello 中,加入 pre_shared_key 擴展,但是EncryptedExtension 子消息中不加入 early_data 擴展。

Client 即使發送握手消息還是帶有 early_data 擴展,但是 Server 導出的密鑰已經是 server/client_handshake_traffic_secret 而不是 client_early_traffic_secret 了,也無法解密 early_data 內容。解密失敗出現錯誤就丟棄這個擴展,忽略它。於是 0-RTT 就會降級到 1-RTT。

0-RTT 握手的流程圖如下:

雖然 TLS 1.3 革命性的提出了 0-RTT 會話恢復模式,但是 0-RTT 存在安全性風險。0-RTT 數據安全性比其他類型的 TLS 數據要弱一些,特別是:

0-RTT 的數據是沒有前向安全性的,它使用的是被提供的 PSK 中導出的密鑰進行加密的。在多個連接之間不能保證不存在重放攻擊。普通的 TLS 1.3 1-RTT 數據為了防止重放攻擊的保護方法是使用 server 下發的隨機數,現在 0-RTT 不依賴於 ServerHello 消息,因此保護措施更差。如果數據與 TLS client 認證或與應用協議裡一起驗證,這一點安全性的考慮尤其重要。這個警告適用於任何使用 early_exporter_master_secret 的情況。

TLS 1.3 0-RTT 中要預防重放攻擊。預防 0-RTT 有 4 種措施:

第一個措施檢查 PSK 中的過期時間,如果過期了,就不處理 early_data 中的請求,並且將握手降級到 1-RTT。

第二個措施是不允許非冪等性的請求出現在 0-RTT 中,如果出現了非冪等性的請求,Server 將會忽略不處理,GET 請求是冪等性的,但是也不能允許後面帶參數,不帶參數的 GET 請求才能允許。

第三個措施是,在請求頭中記錄 PSK binder 的值或者一個隨機值,這個值能保證 0-RTT 的 early_data 全局唯一,這樣就可以防止重放攻擊。當收到 ClientHello 時,Server 首先驗證 PSK binder。然後它會計算 expected_arrival_time,如果它在記錄窗口之外,則拒絕 0-RTT,然後回到 1-RTT 握手。如果 expected_arrival_time 在窗口中,則 Server 檢查它是否記錄了匹配的 ClientHello。如果找到一個,它將使用 "illegal_parameter" alert 消息中止握手或接受 PSK 但拒絕 0-RTT。如果找不到匹配的 ClientHello,則它接受 0-RTT,然後只要 expected_arrival_time 在窗口內,就存儲 ClientHello。Server 也可以實現具有誤報的數據存儲,例如布隆過濾器,在這種情況下,它們必須通過拒絕 0-RTT 來響應明顯的重放,但絕不能中止握手。關於這一個措施,還可能存在多個 binder 的情況,如果是分布式系統,還會存在多個 zone 的問題,具體分析見筆者這篇文章 《TLS 1.3 0-RTT and Anti-Replay》[21]。

第四個措施是,在資料庫裡面記錄所有未完成有效的 ticket,使用一次就刪除掉,如果產生重放攻擊,那麼這個 ticket 必然是資料庫裡面查不到的,那麼就回退到完整握手。

關於 0-RTT 安全性的問題,筆者專門寫了一篇文章探討這個問題,見《TLS 1.3 0-RTT and Anti-Replay》[22]

❞十. 直觀感受 TLS 1.3 會話恢復

這一章,筆者用 wireshark 抓取 TLS 1.3 會話恢復中的數據包,讓讀者直觀感受一下 TLS 1.3 會話恢復流程。

1. PSK 會話恢復

這是 TLS 1.3 會話恢復的完整流程。

上面這 4 個擴展是 TLS 1.3 PSK 會話恢復中 ClientHello 必須配置的。psk_key_exchange_modes、pre_shared_key、key_share、supported_versions。

上面這 3 個擴展是 TLS 1.3 PSK 會話恢復中 ServerHello 必須配置的。pre_shared_key、key_share、supported_versions。

一旦 PSK 校驗完成,Server 就不需要再次發送證書了,直接回應 ChangeCipherSpec、Encrypted Extensions、Finished 即可完成會話恢復。

2. 0-RTT

截止到筆者寫這篇文章為止,當前主流瀏覽器對 TLS 1.3 的支持度如下圖。

Google Chrome Canary 最新 74.0.3702.0 還不能支持 0-RTT 模式,Firefox Nightly 最新 67.0a1 可以支持 0-RTT 模式(在 about:config 中 security.tls.enable_0rtt_data 設置為 true),Safari 最新的 12.0.3 (14606.4.5) 還不能支持 0-RTT 模式。所以筆者只能用 Firefox Nightly 抓取 0-RTT 的包。

當然 OpenSSL 最新版 1.1.1a 的 Client 是支持發送 early_data 的,也就是支持 0-RTT 的,用它來調試 TLS 1.3 0-RTT 也更加方便。

先來看看支持 0-RTT 的 Firefox Nightly 抓到的包是怎麼樣的。

可以發現整個會話恢復過程滿足了 0-RTT 的條件,所以 0-RTT 開啟成功。

在用 OpenSSL 的 Client 來測試測試 0-RTT。

先將必要參數導出來,比如協商的密鑰和 session 信息。

$ openssl s_client -connect halfrost.com:443 -tls1_3 -keylogfile=/Users/ydz/Documents/sslkeylog.log -sess_out=/Users/ydz/Documents/tls13.sess

輸出如下:

CONNECTED(00000006)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
 0 s:CN = halfrost.com
   i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
 2 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
BEGIN CERTIFICATE
MIIEljCCA36gAwIBAgISA9VdA6rPN6mIzBxEPL/3iAICMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAyMTAwMTQxMjJaFw0x
OTA1MTEwMTQxMjJaMBcxFTATBgNVBAMTDGhhbGZyb3N0LmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABA7sYzIwq29BkT1mQ2TSZRPe34BlnuqN65xoLY+A87M8
PpblV0IvNyj4ZdcgiSmSZffocVF6wzck6TmsQ/j2/sujggJyMIICbjAOBgNVHQ8B
Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFOD4YIpf+PkD1Jvy+eayPn0csEi/MB8GA1UdIwQYMBaA
FKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcw
AYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcw
AoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wKQYDVR0RBCIw
IIIMaGFsZnJv\ghfhjghjjbmd3cuaGFsZnJvc3QuY29tMEwGA1UdIARFMEMwCAYG
Z4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMu
bGV0c2VuY3J5cHQub3JnMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUA4mlLribo
6UAJ6IYbtjuD1D7n/nSI+6SPKJMBnd3x2/4AAAFo1UfZTgAABAMARjBEAiAsXJLC
A5uO2R926Dba3fZpV/zvzG9tCPVtTKAeso5bAwIgMXoLRtLqhG5bEcXIpGXJcrd0
6S8tbUdS9YRAIWpMX1oAdgApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0
eAAAAWjVR9lQAAAEAwBHMEUCIHv6NJ9MWMiL+AHxU8ilL3APMmPkUcc03SjBiDaW
Vm6JAiEA5YF/XHKuYH0S0+mqfB+YdT0FIey9wFQObkR4/Qvzla4wDQYJKoZIhvcN
AQELBQADggEBAHU7a+EgzdhrsyD+2ch7AGD1n1TjDfdxkEjmoitN0Tjh4q3jP/IK
7FPs0LBsDRusmtJVK3gZQc9cTEy/om86VQtcnV0LhK83GnFUIuLTEzeTZmnz6Qbs
3KznprZH0DRUbfpmZsDNIfBEOUOXiBR4DpLd3tPVfRkQowmO6o39vM4UOGlB0zIA
g977q97IT6wS9BCEiGmuF0HSjpLfiPhTy9bpl2VGcJVpIy2TS+d4+JWRI7K5BFSz
ncGDzHJ+zGsx4wS+dxuiwaS9hw4c0FG2V4kMFnA+orAa/oTnfwFlRIehTbDBO+rN
TNtjm4yh63M9gInoQEI1REl2EkGcWug6Ijs=
END CERTIFICATE
subject=CN = halfrost.com

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3912 bytes and written 316 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: DECE5063ABC2D1162A5E767C55083FDFFA6A86B64082FE3AD990A213AE
    Session-ID-ctx:
    Resumption PSK: EACCC93ACB3DC420DF5027BEC576EE130D11BF546463034C1BB92B54806057E0C9F5C3DB557AD10D425E
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket:
    0000 - 0b 8d e5 44 b2 62 71 9d-f9 0a ec da f0 d0 6a 0b   ...D.bq..j.
    0010 - 97 5d 63 21 ea 1e 8a 69-01 52 a9 0a 19 bf 5c a3   .]c!...i.R....\.
    0020 - 67 45 a3 a0 28 65 ea 9c-c8 d4 cf df 5d c5 5a be   gE..(e.].Z.
    0030 - 32 45 0d 1e af f7 32 67-4a d8 66 cb b6 cb c8 0e   2E....QgJ.f
    0040 - 6b b8 53 a8 d2 d4 4b 7b-cc a6 cb 52 39 61 20 6d   k.S...K{...R9a m
    0050 - 75 f8 cb 43 11 1d 58 a2-de 2b 74 b0 ca 70 a2 9c   u..C..X..+t..p..
    0060 - 85 6b 1a 00 9a f1 bd 9b-8c b4 5a 41 aa 4b 64 5d   .k...ZA.Kd]
    0070 - 5a 48 23 a6 10 49 4f 61-c9 57 74 f4 56 50 83 1a   ZH#..IOa.Wt.VP..
    0080 - 1b 74 6c ea 09 99 42 f5-d6 3c 6d 4f 5b 98 ca b3   .tl...B..<mO....
    0090 - c7 72 56 5c 6c 67 71 77-8d 68 f7 54 e5 e3 7b d3   .rV\lgqw.h.T..{.
    00a0 - 24 ff 42 0c 3f 12 27 42-7f 9e 0a 4c c2 79 60 45   $.B.?.'B...L.y`E
    00b0 - 2d 77 a2 c8 2f f5 85 34-fa ce 79 ee 0b ea 00 c1   -w../..4..y
    00c0 - 74 33 f0 6c af 7a 1a 55-f8 35 bd 5e 49 66 6f 06   t3.l.z.U.5.^Ifo.
    00d0 - c6 38 ed a6 82 e2 c8 77-99 b7 34 9a 4a 9a 31 40   .8w..4.J.1@
    00e0 - f1 93 a0 94 7f 1e 8d e0-54 29 dc e3 6f 5c 93 21   ...T)..o\.!

    Start Time: 1549886406
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
    Max Early Data: 16384
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: B7E28DE5DF2C95F2E3DE43732E4F9A45A8943ED3856B73CAB5E7260E7
    Session-ID-ctx:
    Resumption PSK: BF2BA2304BEB2B948F7BF6617D0KDRNFB9CD5466DEC1EB9697D2543B7BB913BC7854359D7F5DF7559D67
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket:
    0000 - 0b 8d e5 44 b2 62 71 9d-f9 0a ec da f0 d0 6a 0b   ...D.bq..j.
    0010 - b4 9f cc 17 63 9a 70 c8-63 f8 2e c4 9f d4 a1 f8   ....c.p.c..
    0020 - 22 34 22 03 d0 f9 78 66-a0 d4 2f 62 53 d3 d8 e3   "4"...xf../bS...
    0030 - 55 2c a5 7c 0b 19 b3 fc-77 55 8c de 0b 2d 00 bd   U,.|....wUL..-..
    0040 - b8 fa 2e 00 30 78 c8 dc-35 14 d3 61 f0 69 38 59   ...%0x..5..a.i8Y
    0050 - ee 2a 75 7e 50 34 3f e3-25 04 71 1c 6e c9 c8 20   .*u~P4?.%.q.n..
    0060 - d7 4e 44 b3 69 56 50 23-38 c2 f1 1e ac 10 a7 ff   .ND.iVP#8..
    0070 - 96 cf fe ff 4d 07 7e 08-2d 37 49 78 ab 1d 78 6e   ....M.~.-7Ix..xn
    0080 - 62 4b 99 e7 37 03 3e a2-89 de 61 48 a1 c5 77 18   bK..7.>...aH..w.
    0090 - 6f 1c 95 8a 0d 1d 17 68-88 8a 01 5b f0 dc ea 06   o.h...[....
    00a0 - 98 dc 7e 94 f8 ef 4a 72-ff ba e5 03 07 c7 3d d0   ..~...Jr.=.
    00b0 - c8 91 a6 ae 9a df 92 25-05 63 77 03 b0 bc b4 ab   ..%.c.
    00c0 - 36 cb 0f 8c 5d ec 58 65-7c 97 2a 30 57 4a 96 b9   6...].Xe|.*0WJ..
    00d0 - 60 21 12 76 77 4c 6d 0d-12 0c 50 cc f5 da 54 4e   `!.vwLm...P...TN
    00e0 - 4b 27 5f 1b dd 11 b1 8d-7f e0 37 43 34 a3 88 34   K'_..7C4..4

    Start Time: 1549886406
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
    Max Early Data: 16384
---
read R BLOCK

接下來在復用剛剛的連接,命令如下:

$ openssl s_client -connect halfrost.com:443 -tls1_3 -keylogfile=/Users/ydz/Documents/sslkeylog.log -sess_in=/Users/ydz/Documents/tls13.sess -early_data=/Users/ydz/Documents/req.txt

req.txt 裡面只是簡單的寫一個 GET 請求:

GET / HTTP/1.1
HOST: halfrost.com
Early-Data: 657567765

執行 s_client 以後,輸出如下:

CONNECTED(00000006)
---
Server certificate
BEGIN CERTIFICATE
MIIElzCCA3+gAwIBAgISA604VEs+7Wwch5cNQDshC4t+MA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODEyMDgxMzQzMzhaFw0x
OTAzMDgxMzQzMzhaMBcxFTATBgNVBAMTDGhhbGZyb3N0LmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABA7sYzIwq29BkT1mQ2TSZRPe34BlnuqN65xoLY+A87M8
PpblV0IvNyj4ZdcgiSmSZffocVF6wzck6TmsQ/j2/sujggJzMIICbzAOBgNVHQ8B
Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFOD4YIpf+PkD1Jvy+eayPn0csEi/MB8GA1UdIwQYMBaA
FKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcw
AYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcw
AoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wKQYDVR0RBCIw
IIIMaGFsZnJvc3QuY29tghB3d3cuaGFsZnJvc3QuY29tMEwGA1UdIARFMEMwCAYG
Z4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMu
bGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHUA4mlLribo
73qkwe6lN9vZWu1dJV8+Q41cFLGYMJhDD56x7QIgL+V6g1CQst9UDXobdkAEnjah
KiJWihr/Qn3plzgzjiIAdwApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0
eAAAAWeORhq2AAAEAwBIMEYCIQD1Mf1GtmegyTqIu0S3Q4afNDt0srIFyrtROtn0
jQAV1gIhAJwXIGyMj87kjHtRc/mHJOOCZRSUvoasvWrytCv2dPwXMA0GCSqGSIb3
DQEBCwUAA4IBAQB3sC7jKVGHR8MnAOWnECO/V5Z4oBqbahogwyhOSrbxuutijhyk
8kb3A73Q++Ey150Y+hlNUQStmG9JBGg9pyLG2Yug9p5L13a6VrNaL1VQ1Dq6YgS5
5J8ElsalUgr+9jvTJesdYzfXPdsc8IK67tBXhukqc0/cT3I1QHNwAVru/AKWrkne
H4AcadSeLGe5he2X9OV3JJg+gb/vE90UaVmqwUuSGMzluyBXPMuznTa/+7+31vWV
Q8aWE32X+E5qHSyeLU808mZHYjvKHvuDnNNu6I0KlNcVJf1s0jOQOjgo7hIP/OR4
OlW6ywk07IupV4w07xykP1/tWBsSCviXECcZ
END CERTIFICATE
subject=CN = halfrost.com

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 245 bytes and written 649 bytes
Verification error: unable to get local issuer certificate
---
Reused, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was accepted
Verify return code: 20 (unable to get local issuer certificate)
---

從輸出中可以看到 Early data was accepted。這個時候轉到 wireshark,看抓到的包是怎麼樣的。

可以看到 Client 在 ClientHello 之後,就立即發送了 Application Data。

在 wireshark 中首選項,把下圖中的勾去掉。

配置生效以後,可以看到 Application Data 裡面的請求了。

普通的 GET 請求中 header 中帶了 Early-Data 的值。這個值就會傳給 Server 處理了。

十一. TLS 1.3 的狀態機

TLS 1.3 相對 TLS 1.2 握手流程發生了巨大的變化,所以狀態機也發生了巨大的變化。下面放 2 張狀態流轉圖,最為總結,對應的也是本篇文章的精華。

                              START <----+
               Send ClientHello |        | Recv HelloRetryRequest
          [K_send = early data] |        |
                                v        |
           /                 WAIT_SH ----+
           |                    | Recv ServerHello
           |                    | K_recv = handshake
       Can |                    V
      send |                 WAIT_EE
     early |                    | Recv EncryptedExtensions
      data |           +---+---+
           |     Using |                 | Using certificate
           |       PSK |                 v
           |           |            WAIT_CERT_CR
           |           |        Recv |       | Recv CertificateRequest
           |           | Certificate |       v
           |           |             |    WAIT_CERT
           |           |             |       | Recv Certificate
           |           |             v       v
           |           |              WAIT_CV
           |           |                 | Recv CertificateVerify
           |           +> WAIT_FINISHED <+
           |                  | Recv Finished
           \                  | [Send EndOfEarlyData]
                              | K_send = handshake
                              | [Send Certificate [+ CertificateVerify]]
    Can send                  | Send Finished
    app data   -->            | K_send = K_recv = application
    after here                v
                          CONNECTED

這圖是 Client 在握手流程上的狀態機。如果讀者還不清楚中間的某個步驟,可以對照上文中的內容查缺補漏。

                              START <+
               Recv ClientHello |         | Send HelloRetryRequest
                                v         |
                             RECVD_CH ----+
                                | Select parameters
                                v
                             NEGOTIATED
                                | Send ServerHello
                                | K_send = handshake
                                | Send EncryptedExtensions
                                | [Send CertificateRequest]
 Can send                       | [Send Certificate + CertificateVerify]
 app data                       | Send Finished
 after   -->                    | K_send = application
 here                  +---+---+
              No 0-RTT |                 | 0-RTT
                       |                 |
   K_recv = handshake  |                 | K_recv = early data
 [Skip decrypt errors] |    +-> WAIT_EOED -+
                       |    |       Recv |      | Recv EndOfEarlyData
                       |    | early data |      | K_recv = handshake
                       |    +--+      |
                       |                        |
                       +> WAIT_FLIGHT2 <---+
                                |
                       +---+---+
               No auth |                 | Client auth
                       |                 |
                       |                 v
                       |             WAIT_CERT
                       |        Recv |       | Recv Certificate
                       |       empty |       v
                       | Certificate |    WAIT_CV
                       |             |       | Recv
                       |             v       | CertificateVerify
                       +-> WAIT_FINISHED <---+
                                | Recv Finished
                                | K_recv = application
                                v
                            CONNECTED

這圖是 Server 在握手流程上的狀態機。如果讀者還不清楚中間的某個步驟,可以對照上文中的內容查缺補漏。讀者能理解透上面這 2 張狀態機,TLS 1.3 也就掌握透徹了。

全文完。

Reference:

RFC 8466[23]
TLS1.3 draft-28[24]

Reference[1]

心臟出血: https://en.wikipedia.org/wiki/Heartbleed

[2]

BEAST: https://www.youtube.com/watch?v=-_8-2pDFvmg

[3]

Lucky 13: http://www.isg.rhul.ac.uk/tls/Lucky13.html

[4]

Lucky Microseconds: https://eprint.iacr.org/2015/1129

[5]

POODLE: https://blog.cloudflare.com/sslv3-support-disabled-by-default-due-to-vulnerability/

[6]

《TLS 1.3 Introduction》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Introduction.md#%E4%B8%89tls-13-%E5%92%8C-tls-12-%E4%B8%BB%E8%A6%81%E7%9A%84%E4%B8%8D%E5%90%8C

[7]

《TLS 1.3 Handshake Protocol》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md

[8]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#1-diffie-hellman-parameters

[9]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#2-ecdhe-parameters

[10]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#2-certificate-request

[11]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#1-ocsp-status-and-sct-extensions

[12]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#2-server-certificate-selection

[13]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#3-client-certificate-selection

[14]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#4-receiving-a-certificate-message

[15]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#3-certificate-verify

[16]

[RFC2104]: https://tools.ietf.org/html/rfc2104

[17]

研究表明: http://link.zhihu.com/?target=http%3A//www.isg.rhul.ac.uk/~kp/TLS-AEbounds.pdf

[18]

《Key and Initialization Vector Update》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#3-key-and-initialization-vector-update

[19]

《Pre-Shared Key Extension》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#11-pre-shared-key-extension

[20]

這一章節: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_Handshake_Protocol.md#1-ticket-age

[21]

《TLS 1.3 0-RTT and Anti-Replay》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_0-RTT.md

[22]

《TLS 1.3 0-RTT and Anti-Replay》: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/TLS_1.3_0-RTT.md

[23]

RFC 8466: https://tools.ietf.org/html/rfc8446

[24]

TLS1.3 draft-28: https://tools.ietf.org/html/draft-ietf-tls-tls13-28

相關焦點

  • 深入理解HTTPS
    我們訪問我的個人博客  https://democome.com/,然後用 WireShark 抓包看下 TLS 握手的過程。WireShark抓包TLS握手TLS 握手有 RSA 握手、ECDH 握手。通過我下面的抓包分析,下面的示例中使用的是 ECDH 握手。
  • [原創]SSL/TLS協議及Openssl工具的實現
    3、CA機構對其提供的請求文件提取特徵碼,使用自己的私鑰對特徵碼籤名,生成證書(證書中包含信息請見以下)PKI(Public infrastructure,公鑰基礎設施)四個組件組成籤證機構 : CA註冊機構 : RA證書吊銷列表 : CRL證書存取庫 : CB證書的基本格式(X.509標準)1、證書序列號2、序列號3、籤名算法ID4、發行者名稱5、有效期限6、主體名稱7、主體公鑰8、發行者的唯一標識
  • 握手的禮儀
    早在1905年,孫中山在日本組織同盟會,規定入盟「同志相見之握手暗號」,並親自教導會員如何行握手禮。      兩人相向,握手為禮,是當今世界最為流行的禮節。不僅熟人、朋友,連陌生人、對手,都可能握手。握手常常伴隨寒暄、致意,如你(您)好、歡迎、多謝、保重、再見等。握手禮含義很多,視情而定,分別表示相識、相見、告別、友好、祝賀、感謝、鼓勵、支持、慰問等不同意義。
  • 【HTTPS】攻擊
    如下面的https頭,攻擊這可以控制的部分為get請求地址,想要猜測的部分為Cookie。那麼攻擊者只需要在GET地址處,不斷變換猜測字符串,進行猜測。修改壓縮算法流程,用戶輸入的數據不進行壓縮。隨機添加長度不定的垃圾數據。
  • 溫故知新:Web滲透信息收集
    子域名也就是二級域名,是指頂級域名下的域名。四 埠測試對網站域名對應的真實IP位址進行埠測試,很多有防護不能大批量掃描和漏洞測試,但是放在雲上的網站如果cdn找到真實網站即可大批量掃描。(2)Waf識別:https://github.com/EnableSecurity/wafw00f  kali上自帶wafw00f,一條命令直接使用。建議最好在kali下使用,windows下的使用很麻煩。
  • 常見的HTTPS攻擊方法
    0x00 背景研究常見的https攻擊方法Beast crime breach,並針對https的特性提出一些安全部署https的建議
  • 商務規範握手禮儀
    在現代社會,握手已經成為社交中最基本的禮儀。握手往往表示友好,是一種交流,可以溝通情感,加深雙方的理解、信任,也可以表示一方的尊敬、景仰、祝賀、鼓勵。但握手並不是簡簡單單的兩手相握就行了,他是有著自己的一套禮儀規範。1、握手的順序主人、長輩、上司、女士主動伸出手,客人、晚輩、下屬、男士再相迎握手。
  • 模具工程師必知的產品開發流程
    基於設計人員的構思,通過草圖勾畫方式記錄,繪製各種形態或者標註記錄下設計信息,確定三至四個方向,再由設計師進行深入設計。5. 完成產品平面效果圖:2D效果圖將草圖中模糊的設計結果確定化,精確化。這個過程可以通過CAD軟體來完成。通過這個環節生成精確的產品外觀平面設計圖。
  • Go-Zero 短鏈項目 DevOps 實戰,利用 Drone CI/CD 打通上雲(Kubernetes)迭代流程
    app app \ && apk --no-cache add \ ca-certificates curl netcat-openbsdDockerfile.baseFROM golang:1.15-alpineRUN go env -w GO111MODULE=onRUN go env -w GOPROXY=h
  • SSL和TLS部署實踐指南
    選擇最佳加密套件加密套件是用於在SSL/TLS握手期間協商安全設置的算法的組合。在ClientHello和ServerHello消息交換之後,客戶端發送優先級列表的密碼支持套件。使用會話重用機制由於SSL握手的非對稱運算無論是RSA還是ECDHE,都會消耗性能,故為了提高性能,對於之前已經進行過握手的SSL連接,儘可能減少握手round time trip以及運算。SSL提供2中不同的會話復用機制。
  • 各國面積最直觀對比(下)
    各國面積最直觀對比(上))。也解釋為什麼俄羅斯的面積明明只是我們的兩倍不到,在地圖上卻感覺像是我們的三、四倍那麼大。為了還原和對比各個國家在地圖上的真實尺寸,國外一位大(xian)牛(ren)專門做了個地圖網站,允許用戶自由挪動各個大陸和區域,來感受其在距離赤道不同位置的大小變化。
  • 聊一聊 HTTPS 的工作原理
    ://www.javadoop.com/post/https本文聊聊 HTTPS 的一些東西,和大家扯扯 SSL 證書的整個工作流程。CA 需要做什麼我們在申請一個 https 證書的時候,要在市場上選擇一家 CA 來給你籤發證書,那麼 CA 的工作是什麼呢?CA 要驗證這個域名真的是你的:通常就是通過 DNS 記錄或者就是你在指定 URI 下放置一個特殊文件,讓 CA 可以在外網環境下訪問到它。
  • 第 10 期 4K高清握手圖片素材
  • Android系統啟動流程
    這就涉及到APP的啟動流程了,後續幾篇會細說下。fork了第一個應用進程——Launcher,以及後續的一些系統應用進程,這就到了最上面一層——應用層了。APP層應用層,大家都太熟了,我們做出的應用和系統應用都是在這一層,和用戶之間交互。這一層所有的應用都是通過Zygote進程孵化出來的。
  • Rpe等級:如何客觀地寫下你的訓練感受?
    裡,我建議大家去記錄下自己每一次的訓練,並且講了應當如何去記錄、記錄哪些數據。還不了解的小夥伴們,一定要回去看功課補上!「訓練項目」:指深蹲,伏地挺身,跑步等。「強度」:指訓練重量,動作難度,或者跑步的速度。「容量」:指組數次數安排,或者跑步的距離。
  • 新營小學•中隊篇|走進清明,感受傳統——各中隊活動實踐紀實
    清明節假期,我校各個中隊圍繞「走進清明,感受傳統」的主題,廣泛開展多種形式的清明主題實踐活動,緬懷革命先烈,提高對家鄉文化的認同感
  • EEDrone開源四旋翼從零開始(10)--四旋翼控制分析
    飛行的動力由四個槳葉提供,通過改變四個螺旋槳的轉速對四旋翼進行控制。四個電機產生的總升力可以使四旋翼在垂直方向運動,四個電機升力的差異導致四旋翼俯仰和偏航的變化進而改變運動方向,扭矩力的差異將導致四旋翼的偏航。根據四旋翼的飛行方式將四旋翼的飛行方式劃分為四種基本的控制方式:垂直飛行控制、翻滾控制、俯仰控制和偏航控制。通常四旋翼的飛行模型分為「+」型和「X」型,分別如下圖左和右所示:
  • 2014四校會