TLS 的主要目標是爲通訊的雙方提供一個安全的通道。對下層傳輸的惟一要求是一個可靠的有序的數據流。html
認證: 通道的 Server 端應該老是被認證的;Client 端是可選的被認證。認證能夠經過非對稱算法(例如,RSA, 橢圓曲線數字簽名算法(ECDSA),或 Edwards 曲線數字簽名算法(EdDSA))完成,或經過一個對稱的預共享密鑰(PSK)。git
機密性:在創建完成的通道上發送的數據只能對終端是可見的。TLS 協議並不能隱藏它傳輸的數據的長度,可是終端可以經過填充 TLS 記錄來隱藏長度,以此來提高針對流量分析技術的防禦。github
完整性:在創建完成的通道上面發送數據,不可能存在數據被篡改尚未發現的狀況。即數據一旦被修改,對端會當即發現這個篡改。算法
以上 3 點是必需要保證的,即便網絡攻擊者已經徹底掌握了網絡,發生了 RFC 3552 中發生的狀況。關於 TLS 安全問題,下面有單獨的文章專門再討論。數組
TLS 協議主要由 2 大組件組成:緩存
握手協議
握手協議主要須要處理在通訊雙方之間進行認證的全部流程。包括密鑰協商,參數協商,創建共享密鑰。握手協議被設計用來抵抗篡改;若是鏈接未受到攻擊,則活動攻擊者不該該強制對等方協商不一樣的參數。安全
記錄協議
使用由握手協議創建的參數來保護通訊雙方的流量。記錄協議將流量分爲一系列記錄,每一個記錄獨立地使用密鑰來保護機密性。服務器
TLS 是一個獨立的協議;高層協議能夠透明地位於 TLS 之上。然而,TLS 標準並未指定協議如何加強 TLS 的安全,如何發起 TLS 握手以及如何理解認證證書交換,這些都留給運行在 TLS 之上的協議的設計者和實現者來判斷。網絡
本文檔定義了 TLS 1.3 版。雖然 TLS 1.3 不是直接的與以前的版本兼容,全部版本的TLS都包含一個版本控制機制,即容許客戶端和服務器經過協商,選出通訊過程當中採用的 TLS 版本。session
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.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.3 規範中還定義了一些可選的針對 TLS 1.2 的實現,包括那些不支持 TLS 1.3 的實現。
安全通道所使用的密碼參數由 TLS 握手協議生成。這個 TLS 的子協議,握手協議在 Client 和 Server 第一次通訊時使用。握手協議容許兩端協商一個協議版本,選擇密碼算法,選擇性互相認證,並創建共享的密鑰數據。一旦握手完成,雙方就會使用創建好的密鑰保護應用層數據。
一個失敗的握手或其它的協議錯誤會觸發鏈接的停止,在這以前能夠有選擇地發送一個警報消息,遵循 Alert Protocol 協議。
TLS 1.3 支持 3 種基本密鑰交換模式:
下圖顯示了 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 導出的密鑰保護
握手能夠被認爲有三個階段(見上圖):
在密鑰交換階段,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 參數:
最後,Client 和 Server 交換認證消息。TLS 在每次基於證書的認證時使用相同的消息集,(基於 PSK 的認證是密鑰交換中的一個反作用)特別是:
Certificate: 終端的證書和每一個證書的擴展。 服務器若是不經過證書進行身份驗證,而且若是服務器沒有發送CertificateRequest(由此指示客戶端不該該使用證書進行身份驗證),客戶端將忽略此消息。 請注意,若是使用原始公鑰 [RFC7250] 或緩存信息擴展 [RFC7924],則此消息將不包含證書,而是包含與服務器長期密鑰相對應的其餘值。
CertificateVerify: 使用與證書消息中的公鑰配對的私鑰對整個握手消息進行簽名。若是終端沒有使用證書進行驗證則此消息會被忽略。
Finished: 對整個握手消息的 MAC(消息認證碼)。這個消息提供了密鑰確認,將終端身份與交換的密鑰綁定在一塊兒,這樣在 PSK 模式下也能認證握手。
接收到 Server 的消息以後,Client 會響應它的認證消息,即 Certificate,CertificateVerify (若是須要), 和 Finished。
這時握手已經完成,client 和 server 會提取出密鑰用於記錄層交換應用層數據,這些數據須要經過認證的加密來保護。應用層數據不能在 Finished 消息以前發送數據,必須等到記錄層開始使用加密密鑰以後才能夠發送。須要注意的是 server 能夠在收到 client 的認證消息以前發送應用數據,任何在這個時間點發送的數據,固然都是在發送給一個未被認證的對端。
若是 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還容許基本握手的幾種優化變體,如如下部分所述。
雖然 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 密鑰創建方法。具體來講,它不會阻止能夠觀察到握手過程的攻擊者對密碼/預共享密鑰進行暴力攻擊。
當 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 數據要弱一些,特別是:
0-RTT 數據不能在鏈接中被複制(即 server 不會爲同一鏈接處理相同的數據兩次),而且攻擊者將沒法使 0-RTT 數據假裝起來像 1-RTT數據(由於它受不一樣的密鑰保護)。
關於 0-RTT 的安全性,會單獨有一篇文章來討論。
Reference:
GitHub Repo:Halfrost-Field
Follow: halfrost · GitHub
Source: github.com/halfrost/Ha…