一文讀懂 DTLS 協議

在 WebRTC 中,爲了保證媒體傳輸的安全性,引入了 DTLS 來對通信過程進行加密。DTLS 的作用、原理與 SSL/TLS 類似,都是爲了使得原本不安全的通信過程變得安全。它們的區別點是 DTLS 適用於加密 UDP 通信過程,SSL/TLS 適用於加密 TCP 通信過程,正是由於使用的傳輸層協議不同,造成了它們實現上面的一些差異。

基本概念

對稱密鑰加密技術

對稱密鑰加密的含義是加密過程和解密過程使用的是同一個密鑰。常見的對稱加密算法有 DES、3DES、AES、Blowfish、IDEA、RC5、RC6。

非對稱加密密鑰加密技術

非對稱密鑰加密的含義是加密過程和解密過程使用了不同的密鑰,分別稱爲公開密鑰和私有密鑰。公鑰是衆所周知的,但是密鑰只能爲報文的目標主機所持有。相比對稱加密技術,它的優點是不用擔心密鑰在通信過程中被他人竊取;缺點是需要解碼速度慢,消耗更多的 CPU 資源。常見的非對稱加密算法有 RSA、DSA、DH 等。

數字簽名

數字簽名是附加在報文上的特殊加密校驗碼,即所謂的校驗和,其中利用了非對稱加密密鑰加密技術。數字簽名的主要作用是防止報文被篡改,一旦報文被攻擊者篡改,通過將其與校驗和進行匹配,可以立刻被接收者發現。數字簽名的過程如下圖所示:

發送者 A 將報文摘要(報文通過 SHA-1 等哈希算法生成摘要)通過私有密鑰加密生成簽名,與明文報文一起發給接收者 B,接收者 B 可以通過對收到的信息進行計算後得到兩份報文摘要,比較這兩份報文摘要是否相等可以驗證報文是否被篡改:

  1. 明文報文通過使用與發送端相同的哈希算法生成摘要 1;
  2. 簽名通過公開密鑰解密後生成摘要 2。

數字證書

數字證書是由一些公認可信的證書頒發機構簽發的,不易僞造。包括如下內容:

數字證書可以用於接收者驗證對端的身份。接收者(例如瀏覽器)收到某個對端(例如 Web 服務器)的證書時,會對簽名頒發機構的數字簽名進行檢查,一般來說,接收者事先就會預先安裝很多常用的簽名頒發機構的證書(含有公開密鑰),利用預先的公開密鑰可以對簽名進行驗證。

參考資料

zhangbuhuai.com/post/https-…

SSL/TLS 協議

簡介

要了解 DTLS,首先從我們比較熟悉的 SSL/TLS 開始講起。SSL(Secure Socket Layer) 和 TLS(Transport Layer Security) 簡單理解就是同一件東西的兩個演進階段,同樣都是在應用層和傳輸層之間加入的安全層,最早的時候這個安全層叫做 SSL,由 Netscape 公司推出,後來被 IETF 組織標準化並稱之爲 TLS。SSL/TLS 的作用是爲了解決互聯網通信中存在的三種風險:

  1. 竊聽風險:第三方可以獲知通信內容;
  2. 篡改風險:第三方可以修改通信內容;
  3. 冒充風險:第三方可以冒充他人身份參與通信。

SSL/TLS 協議能夠做到以下這幾點,從而解決上述的三種風險:

  1. 所有信息通過加密傳播,第三方無法竊聽;
  2. 具有數據簽名及校驗機制,一旦被篡改,通信雙方立刻可以發現;
  3. 具有身份證書,防止其他人冒充。

協議棧

SSL/TLS 建立在 TCP 傳輸層上,最常使用 SSL/TLS 的場景是在 HTTPS 中,通過在 HTTP 和 TCP 中加了一層 SSL/TLS,使得不安全的 HTTP 成爲了安全的 HTTPS。協議棧如下圖所示:

SSL/TLS 對於 TCP 傳輸層的加密是通過動態密鑰對數據進行加密實現的,而動態密鑰通過握手流程協商制定。因此在 SSL/TLS 握手過程中需要協商的信息包括:

  1. 協議版本號;
  2. 加密算法,包括非對稱加密算法、動態密鑰算法;
  3. 數字證書,傳輸雙方通過交換證書及簽名校驗來驗證對方身份;
  4. 動態密鑰,由於非對稱加密對性能消耗較大,因此主要的通信過程都是使用動態密鑰進行對稱加密的;對稱加密使用的動態密鑰則在握手過程中通過非對稱加密來傳輸。

握手過程

以 TLS 1.2 爲例:

握手過程如上圖所示,大體來說分成三個過程:明文通信過程、非對成加密通信過程、對稱加密通信過程;

  1. 明文通信過程:在通信兩端首次向對方發送 Hello 消息時,由於雙方都沒有協商好要使用哪種加密方式,因此這個過程中的消息都是使用明文進行發送的。

    a. Client Hello:客戶端首先向服務端發起握手,在握手消息中告訴對方自己支持的 SSL/TLS 版本、加密套件(包括非對稱加密時使用的算法與、非對稱加密時使用的算法、產生密鑰的僞隨機函數 PRF)與數據壓縮算法(TLS1.3 之後就已經沒有這個字段)等;還會攜帶一個 Session ID,因爲握手流程的開銷比較大,使用 Session ID 可以在下一次與 TLS 握手的過程跳過後續繁瑣的握手流程,重用之前的握手結果(如版本號、加密算法套件、master-key 等);併產生一個隨機數 A,也告訴給對方;

    b. Server Hello:服務端響應一個 Server Hello 消息,攜帶協商出來的 TLS/SSL 版本號、加密套件和數據壓縮算法,如果服務端同意客戶端重用上次的會話,就返回一個相同的 Session ID,否則就填入一個全新的 Session ID;

    c. Server Certificate(可選):攜帶服務端數字證書(CA)以驗證服務端身份,裏面攜帶了服務端非對稱加密所使用的公鑰;這步雖然是可選的,但是一般來說客戶端都會要求驗證服務端的身份,在大多數情況下這步都會執行;

    d. Server Key Exchange(可選):在使用某些非對稱加密算法(例如 DH 算法)的情況下,Server Certificate 裏的信息是不足夠的,或者 Server Certificate 在某些通信過程中直接被省略了(沒有驗證服務端身份),需要 Server Key Exchange 裏的額外信息來幫助客戶端生成 pre-master key;

    e. Client Sertificate Request(可選):在有些安全性要求高的場景,例如銀行支付等,不僅需要驗證服務端的身份,還需要驗證客戶端的身份,這時候服務端就會要求客戶端提供客戶端的身份證書;

    f. Server Hello Done:表明 Server Hello 結束;

    g. Client Certificate(可選):如果服務端要求客戶端提供數字證書以驗證身份,則客戶端發送自己的身份證書給服務端;

  2. 非對稱加密通信過程:由於非對稱加密通信的性能較差,在實際的通信過程中其實使用的是對稱加密通信,爲了保證對稱加密通信過程的安全性,也就是需要避免對稱加密密鑰被竊取,這個密鑰在協商過程中使用非對稱加密來進行加密。

    a. Client Key Exchange:客戶端在驗證服務端的身份證書後,會取出其中的服務端公鑰,產生一個隨機數 C,作爲 pre-master key,在本地使用之前的隨機數 A、B 和這次生成的 C 共同生成對稱加密密鑰 master-key;使用服務端公鑰對 pre-master key 加密後發送給服務端;

    b. Certificate Verify(可選):如果服務端要求客戶端提供客戶端證書,那麼客戶端在發送 Client Key Exchange 之後必須馬上發送 Certificate Verify,其中的內容是客戶端使用自己的私鑰加密的一段數據,提供給服務端用客戶端的公鑰來進行解密驗證。之所以需要這一步是爲了確保客戶端發送的證書確實是它自己的證書;

    c. Client Change Cipher Spec:提示服務端隨後使用 master key 來進行對稱加密通信;

    d. Client Handshake Finished: 表明客戶端側 SSL/TLS 握手結束;

    e. Server Change Cipher Spec:提示客戶端隨後使用 master key 來進行對稱加密通信;

    f. Server Handshake Finished:表明服務端側 SSL/TLS 握手結束;

  3. 對稱加密通信過程:通過上述握手過程協商出對稱加密算法及使用的對稱加密密鑰之後,隨後的通信過程,也就是實際的應用通信過程,都使用的是對稱加密。

握手消息格式

SSL/TLS 的握手消息格式如下所示,消息類型已經在上文握手過程中分別進行了解釋;結構體中包含 消息類型、消息長度、消息體。

enum {
       hello_request(0), client_hello(1), server_hello(2),
       certificate(11), server_key_exchange (12),
       certificate_request(13), server_hello_done(14),
       certificate_verify(15), client_key_exchange(16),
       finished(20)
       (255)
   } HandshakeType;

   struct {
       HandshakeType msg_type;
       uint24 length;
       select (HandshakeType) {
           case hello_request:       HelloRequest;
           case client_hello:        ClientHello;
           case server_hello:        ServerHello;
           case certificate:         Certificate;
           case server_key_exchange: ServerKeyExchange;
           case certificate_request: CertificateRequest;
           case server_hello_done:   ServerHelloDone;
           case certificate_verify:  CertificateVerify;
           case client_key_exchange: ClientKeyExchange;
           case finished:            Finished;
       } body;
   } Handshake;複製代碼

每種消息都會有自己獨立的消息體,消息體中的內容就是握手過程中提到的消息中需要攜帶的一些信息,以 ClientHello 爲例,其中需要包含 SSL/TLS 版本號、隨機數、Session ID、可選的加密套件、可選的壓縮算法:

struct {
           ProtocolVersion client_version;
           Random random;
           SessionID session_id;
           CipherSuite cipher_suites<2..2^16-1>;
           CompressionMethod compression_methods<1..2^8-1>;
       } ClientHello;複製代碼

參考資料

RFC:
tools.ietf.org/html/rfc224… TLS 1.0
tools.ietf.org/html/rfc434… TLS 1.1
tools.ietf.org/html/rfc524… TLS 1.2(本文主要參考)
tools.ietf.org/html/rfc844… TLS 1.3
論壇資料:
www.cnblogs.com/littleatp/p…
segmentfault.com/a/119000002…
halfrost.com/https_tls1-…

DTLS 協議

簡介

DTLS 的全稱爲 Datagram Transport Layer Security,從名字上就可以看出它和 TLS 的區別就在於多了一個 “Datagram”,因爲我們把使用 UDP 傳輸的報文叫做 “Datagram”,也就是 DTLS 是適用於 UDP 傳輸過程的加密協議。 DTLS 在設計上儘可能複用 TLS 現有的代碼,並做一些小的修改來適配 UDP 傳輸。DTLS 與 TLS 具備了同樣的安全機制和防護等級,同樣能夠防止消息竊聽、篡改,以及身份冒充等問題。在版本上,DTLS 和 TLS 也有一定的對應關係,如下:

沒有 DTLS 1.1 應當是爲了和 TLS 版本號相一致。

協議棧

在 WebRTC 中,通過引入 DTLS 對 RTP 進行加密,使得媒體通信變得安全。通過 DTLS 協商出加密密鑰之後,RTP 也需要升級爲 SRTP,通過密鑰加密後進行通信。協議棧如下圖所示:

握手過程

TLS 1.2 及之前都沒有嘗試解決 DoS 攻擊的問題,直到 TLS 1.3 才通過加入了 HelloRetryRequest 和 Cookie 來解決 DoS 攻擊的問題(並且在 TLS 1.3 的 RFC 中提到主要用於非面向連接的通道,也就是 UDP 連接)。相對 TCP 來說,UDP 連接對 DoS 攻擊更加敏感,因此 DTLS 在 1.0 版本就加入了 HelloVerifyRequest 和 Cookie,用於服務端對客戶端的二次校驗,避免 DoS 攻擊。
同樣以 DTLS 1.2 舉例(TLS 1.3 和 DTLS 1.3 的流程已經很接近了),相比 TLS 1.2,DTLS 1.2 大部分步驟都是一樣的,只是在服務端多了一步 HelloVerifyRequest,客戶端因此也多了第二次的 ClientHello,如下圖所示:

服務端在首次收到客戶端發送的 Client Hello 之後,只會生成一個 Cookie,不進行任何其他的操作,並給客戶端發送 HelloVerifyRequest 消息,帶上這個 Cookie。只有當客戶端重新發送一次 Client Hello,並帶上服務端發送的這個 Cookie 後,服務端纔會繼續握手過程。

握手消息格式

DTLS 的握手消息格式如下所示,可以看到相比 SSL/TLS 的握手協議,DTLS 在消息類型中多了 HelloVerifyRequest 這種消息(在握手過程中介紹過),在結構體中多了 message_seq、fragment_offset、fragment_length 三個字段。
SSL/TLS 基於 TCP,因此不需要操心重放、亂序、丟包的問題,可靠傳輸由 TCP 做了保證;而 DTLS 基於 UDP,UDP 是一種盡力而爲的協議,因此 DTLS 需要自己處理重放、亂序、丟包的問題。DTLS 在複用大部分 TLS 的基礎上做了一些小改動,在握手消息中增加了三個字段 message_seq、fragment_offset、fragment_length 三個字段,具體的功能在下一節中講述。

enum {
     hello_request(0), client_hello(1), server_hello(2),
     hello_verify_request(3),                   // New field
     certificate(11), server_key_exchange (12),
     certificate_request(13), server_hello_done(14),
     certificate_verify(15), client_key_exchange(16),
     finished(20)(255) } HandshakeType;

   struct {
     HandshakeType msg_type;
     uint24 length;
     uint16 message_seq;                        // New field
     uint24 fragment_offset;                    // New field
     uint24 fragment_length;                    // New field
     select (HandshakeType) {
       case hello_request: HelloRequest;
       case client_hello:  ClientHello;
       case server_hello:  ServerHello;
       case hello_verify_request: HelloVerifyRequest;// New field
       case certificate:Certificate;
       case server_key_exchange: ServerKeyExchange;
       case certificate_request: CertificateRequest;
       case server_hello_done:ServerHelloDone;
       case certificate_verify:  CertificateVerify;
       case client_key_exchange: ClientKeyExchange;
       case finished: Finished;
     } body; } Handshake;複製代碼

每種消息同樣都會有自己獨立的消息體,消息體中的內容就是握手過程中提到的消息中需要攜帶的一些信息,大部分的消息體內容與 SSL/TLS 相同,區別有兩點:

  1. 多了 HelloVerifyRequest 消息,需要攜帶 Cookie,消息體如下所示:
struct {
        ProtocolVersion server_version;
        opaque cookie<0..32>;
      } HelloVerifyRequest;複製代碼
  1. ClientHello 多了 Cookie 字段,因爲第二次 ClientHello 需要攜帶 Cookie 信息:
struct {
        ProtocolVersion client_version;
        Random random;
        SessionID session_id;
        opaque cookie<0..32>;                             // New field
        CipherSuite cipher_suites<2..2^16-1>;
        CompressionMethod compression_methods<1..2^8-1>;
      } ClientHello;複製代碼

與 SSL/TLS 在實現上的區別

在上文的握手過程和握手協議中已經講述了一些 DTLS 與 SSL/TLS 的區別。此外,DTLS 針對重複、亂序、丟包等問題增加了一些防護機制。

握手防護機制

重傳

TCP 天然的重傳機制保證了消息不會丟失,而 UDP 對此沒有任何保證。因此 DTLS 額外增加了超時重傳機制來確定握手消息到達,流程如下:

以握手的第一階段舉例,客戶端發送 Client Hello(不帶 Cookie,區別於握手流程中的第二次 Client Hello)之後,啓動一個定時器,等待服務端返回 HelloVerifyRequest,如果超過了定時器時間客戶端還沒有收到 HelloVerifyRequest,那麼客戶端就會知道要麼是 Client Hello 消息丟了要麼是 Hello Verify Request 消息丟了,客戶端就會再次發送相同的 Client Hello 消息,即使服務端確實發送了 Hello Verify Request 還是收到了 Client Hello 消息,它也知道是需要重傳,並再次發送 Hello Verify Request 消息,同樣地,服務端也會啓動定時器來等待嚇一條消息。

序列號

TCP 消息中自帶了序列號 seq,並處理了亂序的問題,而 UDP 對亂序問題也沒有任何保證。爲了保證握手消息的有序性,DTLS 在握手報文中增加了 message_seq 字段便於接收方處理亂序消息。接收方直接處理屬於當前步驟的消息,提供一個緩存隊列來緩存提前到達的消息。

消息重放檢測

消息重放也是 DoS 攻擊的一種,攻擊者可以截取發送者發送的數據,並直接原封不動地發給接收方,來達到欺騙接收方的目的。 DTLS 增加了類似 IPsec AH/ESP 的消息重放檢測,使用一個 bitmap 滑動窗口來接收消息,結合消息本身的序號,bitmap 可以判斷該消息是否是太老的消息,是的話則直接拋棄。這個功能在 DTLS 中是可選的,因爲某些 UDP 報文的重複只是單純因爲網絡錯誤。

報文大小限制

TCP 面向字節流,而 UDP 面向報文。因此 TCP 會自動將報文進行拆分和組裝,無需上層操心;而 UDP 報文如果超過了 MTU(鏈路層的最大傳輸單元) 限制,會在 IP 層被強制分片,使得每一片大小都小於 MTU,接收方 IP 層需要進行數據報的重組,這樣就會多做許多工作,更麻煩的地方在於只要其中一片丟失就會造成重組失敗,造成整個 UDP 報文丟失。 因此 DTLS 直接在 UDP 之上就對握手消息做了分段,握手報文中的 fragment_offset 和 fragment_length 就是爲了這個目的,分別代表這段報文相對消息起始的偏移量以及這段報文的長度。

在上文的握手過程中 TLS 1.2 及之前都沒有嘗試解決 DoS 攻擊的問題,直到 TLS1.3 才通過加入了 HelloRetryRequest 和 Cookie 來解決 DoS 攻擊的問題(並且在 TLS 1.3 的 RFC 中提到主要用於非面向連接的通道,也就是 UDP 連接)。相對 TCP 來說,UDP 連接對 DoS 攻擊更加敏感,因爲 TCP 可以使用 SYN Cookie 的機制來防範 DoS 攻擊。 如果在 UDP 上直接使用 TLS 的握手方式,就可能發生以下兩種情況:

  1. 攻擊者可以通過發送一系列握手啓動請求來消耗服務器上的資源,例如服務端可能會爲此分配緩衝區,並且加密過程也是一個非常消耗 CPU 資源的操作;
  2. 攻擊者可以僞造受害客戶端的 IP 地址向服務端發起 DTLS 握手,迫使服務端發送 Certificate 消息給受害客戶端,上文提到過 Certificate 攜帶了很多信息,包括數字證書等,所以這個消息會非常大,使得受害客戶端不得不接受大量無用消息。

因此 DTLS 在 1.0 版本就加入了 HelloVerifyRequest 和 Cookie,用於服務端對客戶端的二次校驗,避免 DoS 攻擊。具體實現方式如下:

  1. 當客戶端首次給服務端發送 Client Hello 時,服務端只會生成一個 Cookie 並通過 HelloVerifyRequest 發送給客戶端,不會執行分配緩衝區等操作,直到收到帶上相同 Cookie 的 Client Hello 纔會繼續握手,可以使得僞造 IP 的攻擊難以實現(使用真實 IP 的 DoS 攻擊無能爲力);
  2. HelloVerifyRequest 足夠小,即使服務端被攻擊者當槍使來攻擊其他機器,也不會造成大量數據發送。

加密方式

由於 SSL/TLS 依賴 TCP,所以 SSL/TLS 有如下幾個特性:

  1. SSL/TLS 不能獨立解密單個封包,SSL/TLS 對於封包的認證需要序號作爲輸入,在 SSL/TLS 中並未直接傳遞序號,因爲 TCP 是可靠的,所以 TLS 的兩端各自維護自身的收發序號;
  2. SSL/TLS 的某些加密算法不是獨立解密的,需要依賴上個封包,典型的就是 RC4 流加密算法。

由於 UDP 本身的不可靠,爲了解決上面提到的第一個問題,DTLS 在每條記錄中顯式攜帶的序號作爲解碼的輸入;而對於第二個問題,DTLS 的現狀是不能支持 RC4 等流式加密算法。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://blog.51cto.com/u_15087084/2598254