TLS 1.3 Introduction

一縷殤流化隱半邊冰霜發表於2019-02-08

一、TLS 協議的目的

TLS 的主要目標是為通訊的雙方提供一個安全的通道。對下層傳輸的唯一要求是一個可靠的有序的資料流。

  • 認證: 通道的 Server 端應該總是被認證的;Client 端是可選的被認證。認證可以通過非對稱演算法(例如,RSA, 橢圓曲線數字簽名演算法(ECDSA),或 Edwards 曲線數字簽名演算法(EdDSA))完成,或通過一個對稱的預共享金鑰(PSK)。

  • 機密性:在建立完成的通道上傳送的資料只能對終端是可見的。TLS 協議並不能隱藏它傳輸的資料的長度,但是終端能夠通過填充 TLS 記錄來隱藏長度,以此來提升針對流量分析技術的防護。

  • 完整性:在建立完成的通道上面傳送資料,不可能存在資料被篡改還沒有發現的情況。即資料一旦被修改,對端會立即發現這個篡改。

以上 3 點是必須要保證的,即使網路攻擊者已經完全掌握了網路,發生了 RFC 3552 中發生的情況。關於 TLS 安全問題,下面有單獨的文章專門再討論。

二、TLS 協議的組成

TLS 協議主要由 2 大元件組成:

  • 握手協議
    握手協議主要需要處理在通訊雙方之間進行認證的所有流程。包括金鑰協商,引數協商,建立共享金鑰。握手協議被設計用來抵抗篡改;如果連線未受到攻擊,則活動攻擊者不應該強制對等方協商不同的引數。

  • 記錄協議
    使用由握手協議建立的引數來保護通訊雙方的流量。記錄協議將流量分為一系列記錄,每個記錄獨立地使用金鑰來保護機密性。

TLS 是一個獨立的協議;高層協議可以透明地位於 TLS 之上。然而,TLS 標準並未指定協議如何增強 TLS 的安全,如何發起 TLS 握手以及如何理解認證證書交換,這些都留給執行在 TLS 之上的協議的設計者和實現者來判斷。

本文件定義了 TLS 1.3 版。雖然 TLS 1.3 不是直接的與之前的版本相容,所有版本的TLS都包含一個版本控制機制,即允許客戶端和伺服器通過協商,選出通訊過程中採用的 TLS 版本。

TLS 1.3 的標準中取代和廢除了以前版本的 TLS,包括 1.2 版本RFC5246 The Transport Layer Security (TLS) Protocol Version 1.2。也廢除了在 RFC5077 Transport Layer Security (TLS) Session Resumption without Server-Side State 裡面定義的 TLS ticket 機制,並用 Pre-Shared Key (PSK) 機制取代它。由於 TLS 1.3 改變了金鑰的匯出方式,它更新了RFC5705 Keying Material Exporters for Transport Layer Security (TLS)。它也改變了線上證書狀態協議(OCSP)訊息的傳輸方式,因此更新了RFC6066 https://tools.ietf.org/html/rfc6066,廢除了RFC6961 he Transport Layer Security (TLS) Multiple Certificate Status Request Extension,如 OCSP Status and SCT Extensions 這一章節所述。

三、TLS 1.3 和 TLS 1.2 主要的不同

下面描述了 TLS 1.2 和 TLS 1.3 的主要的差異。除去這些主要的差別以外,還有很多細微的不同。

  • 已支援的對稱演算法的列表已經去除了已經不再安全的演算法了。列表保留了所有使用“帶關聯資料的認證加密”(AEAD)演算法。 加密套件概念已經被改變,從記錄保護演算法(包括金鑰長度)和一個用於金鑰生成函式的 hash 和 HMAC 中分離為:認證、金鑰交換機制。

  • 增加了一個 0-RTT 模式,為一些應用資料在連線建立階段節省了一次往返,這是以犧牲一定的安全特性為代價的。關於 0-RTT 的安全問題,下面會專門討論

  • 靜態 RSA 和 Diffie-Hellman 密碼套件已經被刪除;所有基於公鑰的金鑰交換演算法現在都能提供前向安全。

  • 所有 ServerHello 之後的握手訊息現在都已經加密。新引入的 EncryptedExtension 訊息允許之前在 ServerHello 中以明文傳送的各種擴充套件同樣享有保密性。

  • 金鑰匯出函式被重新設計。新的設計使得密碼學家能夠通過改進的金鑰分離特性進行更容易的分析。基於 HMAC 的提取 --- 擴充套件金鑰匯出函式(HMAC-based Extract-and-Expand Key Derivation Function,HKDF)被用作一個基礎的原始元件(primitive)。

  • 握手狀態機已經進行了重大調整,以便更具一致性,刪除多餘的訊息如 ChangeCipherSpec (除了由於中介軟體相容性被需要時)。

  • 橢圓曲線演算法已經屬於基本的規範,且包含了新的簽名演算法,如 EdDSA。TLS 1.3 刪除了點格式協商以利於每個曲線使用單點格式。

  • 其它的密碼學改進包括改變 RSA 填充以使用 RSA 概率簽名方案(RSASSA-PSS),刪除壓縮,DSA,和定製 DHE 組。

  • TLS1.2 的版本協商機制被廢棄。支援在擴充套件中使用版本列表。這增加了與不正確地實現版本協商的 Server 的相容性。

  • 帶有和不帶 Server 端狀態的會話恢復以及 TLS 早期版本的基於 PSK 密碼套件已經被一個單獨的新 PSK 交換所取代。

  • 酌情更新引用以指向最新版本的 RFC(例如,RFC 5280 而不是 RFC 3280)。

四、對 TLS 1.2 產生影響的改進

TLS 1.3 規範中還定義了一些可選的針對 TLS 1.2 的實現,包括那些不支援 TLS 1.3 的實現。

  • TLS 1.3 中定義的版本降級保護機制
  • RSASSA-PSS 簽名方案
  • ClientHello 中 “supported_versions” 的擴充套件可以被用於協商 TLS 使用的版本,它優先於 ClientHello 中的 legacy_version 域。
  • "signature_algorithms_cert" 擴充套件允許一個 Client 顯示它使用哪種簽名演算法驗證 X.509 證書。

五、TLS 1.3 協議概覽

安全通道所使用的密碼引數由 TLS 握手協議生成。這個 TLS 的子協議,握手協議在 Client 和 Server 第一次通訊時使用。握手協議允許兩端協商一個協議版本,選擇密碼演算法,選擇性互相認證,並建立共享的金鑰資料。一旦握手完成,雙方就會使用建立好的金鑰保護應用層資料。

一個失敗的握手或其它的協議錯誤會觸發連線的中止,在這之前可以有選擇地傳送一個警報訊息,遵循 Alert Protocol 協議。

TLS 1.3 支援 3 種基本金鑰交換模式:

  • (EC)DHE (基於有限域或橢圓曲線的 Diffie-Hellman)
  • PSK - only
  • PSK with (EC)DHE

下圖顯示了 TLS 握手的全部流程:

       Client                                           Server

Key  ^ ClientHello
Exch | + key_share*
     | + signature_algorithms*
     | + psk_key_exchange_modes*
     v + pre_shared_key*       -------->
                                                  ServerHello  ^ Key
                                                 + key_share*  | Exch
                                            + pre_shared_key*  v
                                        {EncryptedExtensions}  ^  Server
                                        {CertificateRequest*}  v  Params
                                               {Certificate*}  ^
                                         {CertificateVerify*}  | Auth
                                                   {Finished}  v
                               <--------  [Application Data*]
     ^ {Certificate*}
Auth | {CertificateVerify*}
     v {Finished}              -------->
       [Application Data]      <------->  [Application Data]

複製程式碼

+ 表示的是在以前標註的訊息中傳送的值得注意的擴充套件
* 表示可選的或者依賴一定條件的訊息/擴充套件,它們不總是傳送
() 表示訊息由從 Client_early_traffic_secret 匯出的金鑰保護
{} 表示訊息使用從一個 [sender]_handshake_traffic_secret 匯出的金鑰保護
[] 表示訊息使用從 [sender]_application_traffic_secret_N 匯出的金鑰保護

握手可以被認為有三個階段(見上圖):

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

在金鑰交換階段,Client 會傳送 ClientHello 訊息,其中包含了一個隨機 nonce(ClientHello.random);它提供了協議版本,一個對稱密碼/HKDF hash 對的列表;一個 Diffie-Hellman 金鑰共享集合或一個預共享金鑰標籤(在 "key_share" 擴充套件中)集合,或二者都有;和可能的其它擴充套件。

Server 處理 ClientHello 併為連線確定合適的密碼引數。然後它會以自己的 ServerHello 作為響應,其中表明瞭協商好的連線引數。ClientHello 和 ServerHello 合在一起來確定共享金鑰。如果已經建立的 (EC)DHE 金鑰正在被使用,則 ServerHello 中會包含一個 ”key_share” 擴充套件,和這個擴充套件一起的還有 Server 的臨時 Diffie-Hellman 共享引數,這個共享引數必須與 Client 的一個共享引數在相同的組裡。如果使用的是 PSK 金鑰,則 ServerHello 中會包含一個 "pre_shared_key" 擴充套件以表明 Client 提供的哪一個 PSK 被選中。需要注意的是實現上可以將 (EC)DHE 和 PSK 一起使用,這種情況下兩種擴充套件都需要提供。

隨後 Server 會傳送兩個訊息來建立 Server 引數:

  • EncryptedExtensions: 對 ClientHello 擴充套件的響應,不需要確定加密引數,而不是特定於各個證書的加密引數。
  • CertificateRequest: 如果需要基於證書的客戶端身份驗證,則所需引數是證書。 如果不需要客戶端認證,則省略此訊息。

最後,Client 和 Server 交換認證訊息。TLS 在每次基於證書的認證時使用相同的訊息集,(基於 PSK 的認證是金鑰交換中的一個副作用)特別是:

  • Certificate: 終端的證書和每個證書的擴充套件。 伺服器如果不通過證書進行身份驗證,並且如果伺服器沒有傳送CertificateRequest(由此指示客戶端不應該使用證書進行身份驗證),客戶端將忽略此訊息。 請注意,如果使用原始公鑰 [RFC7250] 或快取資訊擴充套件 [RFC7924],則此訊息將不包含證書,而是包含與伺服器長期金鑰相對應的其他值。

  • CertificateVerify: 使用與證書訊息中的公鑰配對的私鑰對整個握手訊息進行簽名。如果終端沒有使用證書進行驗證則此訊息會被忽略。

  • Finished: 對整個握手訊息的 MAC(訊息認證碼)。這個訊息提供了金鑰確認,將終端身份與交換的金鑰繫結在一起,這樣在 PSK 模式下也能認證握手。

接收到 Server 的訊息之後,Client 會響應它的認證訊息,即 Certificate,CertificateVerify (如果需要), 和 Finished。

這時握手已經完成,client 和 server 會提取出金鑰用於記錄層交換應用層資料,這些資料需要通過認證的加密來保護。應用層資料不能在 Finished 訊息之前傳送資料,必須等到記錄層開始使用加密金鑰之後才可以傳送。需要注意的是 server 可以在收到 client 的認證訊息之前傳送應用資料,任何在這個時間點傳送的資料,當然都是在傳送給一個未被認證的對端。

1. 錯誤的 DHE 共享

如果 client 沒有提供一個充分的 ”key_share” 擴充套件(例如,它只包含 server 不接受或不支援的 DHE 或 ECDHE 組),server 會使用一個 HelloRetryRequest 來糾正這個不匹配問題,client 需要使用一個合適的 ”key_share” 擴充套件來重啟握手,如下圖所示。如果沒有通用的密碼引數能夠協商,server 必須發出一個適當的警報來中止握手。

        Client                                               Server

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

複製程式碼

如上圖,一個帶有不匹配引數的完整握手過程的訊息流程

注意,這個握手過程包含初始的 ClientHello/HelloRetryRequest 交換;它不能被新的 ClientHello 重置。

TLS還允許基本握手的幾種優化變體,如以下部分所述。

2. 複用和預共享金鑰(Pre-Shared Key,PSK)

雖然 TLS 預共享金鑰(PSK)能夠在帶外建立,預共享金鑰也能在一個之前的連線中建立然後重用(會話恢復)。一旦一次握手完成,server 就能給 client 傳送一個與一個獨特金鑰對應的 PSK 金鑰,這個金鑰來自初次握手。然後 client 能夠使用這個 PSK 金鑰在將來的握手中協商相關 PSK 的使用。如果 server 接受它,新連線的安全上下文在密碼學上就與初始連線關聯在一起,從初次握手中得到的金鑰就會用於裝載密碼狀態來替代完整的握手。在 TLS 1.2 以及更低的版本中,這個功能由 "session IDs" 和 "session tickets" [RFC5077]來提供。這兩個機制在 TLS 1.3 中都被廢除了。

PSK 可以與 (EC)DHE 金鑰交換演算法一同使用以便使共享金鑰具備前向安全,或者 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]

複製程式碼

當 server 通過一個 PSK 進行認證時,它不會傳送一個 Certificate 或一個 CertificateVerify 訊息。當一個 client 通過 PSK 想恢復會話的時候,它也應當提供一個 "key_share" 給 server,以允許 server 拒絕恢復會話的時候降級到重新回答一個完整的握手流程中。Server 響應 "pre_shared_key" 擴充套件,使用 PSK 金鑰協商建立連線,同時響應 "key_share" 擴充套件來進行 (EC)DHE 金鑰建立,由此提供前向安全。

當 PKS 在帶外提供時,PSK 金鑰和與 PSK 一起使用的 KDF hash 演算法也必須被提供。

注意:當使用一個帶外提供的預共享金鑰時,一個關鍵的考慮是在金鑰生成時使用足夠的熵,就像 [RFC4086] 中討論的那樣。從一個口令或其它低熵源匯出的一個共享金鑰並不安全。一個低熵密碼,或口令,易遭受基於 PSK 繫結器的字典攻擊。指定的 PSK 金鑰並不是一個基於強口令的已認證的金鑰交換,即使使用了 Diffie-Hellman 金鑰建立方法。具體來說,它不會阻止可以觀察到握手過程的攻擊者對密碼/預共享金鑰進行暴力攻擊。

3. 0-RTT 資料

當 client 和 server 共享一個 PSK(從外部獲得或通過一個以前的握手獲得)時,TLS 1.3 允許 client 在第一個傳送出去的訊息中攜帶資料("early data")。Client 使用這個 PSK 來認證 server 並加密 early data。

如下圖所示,0-RTT 資料在第一個傳送的訊息中被加入到 1-RTT 握手過程中。握手的其餘訊息與帶 PSK 會話恢復的 1-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 的資訊流

+ 標明是在以前標註的訊息中傳送的值得注意的擴充套件
* 表示可選的或者依賴一定條件的訊息/擴充套件,它們不總是傳送
() 表示訊息由從client_early_traffic_secret匯出的金鑰保護
{} 表示訊息使用從一個[sender]_handshake_traffic_secret匯出的金鑰保護
[]表示訊息使用從[sender]_application_traffic_secret_N匯出的金鑰保護

0-RTT 陣列安全性比其他型別的 TLS 資料要弱一些,特別是:

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

0-RTT 資料不能在連線中被複制(即 server 不會為同一連線處理相同的資料兩次),並且攻擊者將無法使 0-RTT 資料偽裝起來像 1-RTT資料(因為它受不同的金鑰保護)。

關於 0-RTT 的安全性,會單獨有一篇文章來討論。


Reference:

RFC 8446

GitHub Repo:Halfrost-Field

Follow: halfrost · GitHub

Source: github.com/halfrost/Ha…

相關文章