詳解 WebRTC 傳輸安全機制:一文讀懂 DTLS 協議
作者|進學
審校|泰一
DTLS (Datagram Transport Layer Security) 基於 UDP 場景下數據包可能丟失或重新排序的現實情況下,為 UDP 訂製和改進的 TLS 協議。在 WebRTC 中使用 DTLS 的地方包括兩部分:協商和管理 SRTP 密鑰和為 DataChannel 提供加密通道。
本文結合實際數據包分析 WebRTC 使用 DTLS 進行 SRTP 密鑰協商的流程。並對在實際項目中使用 DTLS 遇到的問題進行總結。
DTLS 協議簡介
在分析 DTLS 在 WebRTC 中的應用之前,先介紹下 DTLS 協議的基本原理。DTLS 協議由兩層組成: Record 協議 和 Handshake 協議
- Record 協議:使用對稱密鑰對傳輸數據進行加密,並使用 HMAC 對數據進行完整性校驗,實現了數據的安全傳輸。
- Handshake 協議:使用非對稱加密演算法,完成 Record 協議使用的對稱密鑰的協商。
HandShake
TLS 握手協議流程如下,參考 RFC5246
DTLS 握手協議流程如下,參考 RFC6347
TLS 和 DTLS 的握手過程基本上是一致的,差別以及特別說明如下:
- DTLS 中 HelloVerifyRequest 是為防止 DoS 攻擊增加的消息。
- TLS 沒有發送 CertificateRequest,這個也不是必須的,是反向驗證即伺服器驗證客戶端。
- DTLS 的 RecordLayer 新增了 SequenceNumber 和 Epoch,以及 ClientHello 中新增了 Cookie,以及 Handshake 中新增了 Fragment 資訊(防止超過 UDP 的 MTU),都是為了適應 UDP 的丟包以及容易被攻擊做的改進。參考 RFC 6347
- DTLS 最後的 Alert 是將客戶端的 Encrypted Alert 消息,解密之後直接響應給客戶端的,實際上 Server 應該回應加密的消息,這裡我們的伺服器回應明文是為了解析客戶端加密的那個 Alert 包是什麼。
RecordLayer 協議是和 DTLS 傳輸相關的協議,UDP 之上是 RecordLayer,RecordLayer 之上是 Handshake 或 ChangeCipherSpec 或 ApplicationData。RecordLayer 協議定義參考 RFC4347,實際上有三種 RecordLayer 的包:
DTLSPlaintext
,DTLS 明文的 RecordLayer。DTLSCompressed
,壓縮的數據,一般不用。DTLSCiphertext
,加密的數據,在 ChangeCipherSpec 之後就是這種了。
沒有明確的欄位說明是哪種消息,不過可以根據上下文以及內容判斷。比如 ChangeCipherSpec 是可以通過類型,它肯定是一個 Plaintext。除了 Finished 的其他握手,一般都是 Plaintext。
SRTP 密鑰協商
角色協商
在 DTLS 協議,通訊的雙方有 Client
和 Server
之分。在 WebRTC 中 DTLS 協商的身份是在 SDP
中描述的。描述如下,參考 SDP-Anatomy 中 DTLS 參數
a=setup:active
setup
屬性在 RFC4145,
setup:active,作為 client,主動發起協商
setup:passive, 作為 sever,等待發起協商
setup:actpass, 作為 client,主動發起協商。作為 server,等待發起協商。
演算法協商 – Hello 消息
ClienHello 和 ServerHello 協商 DTLS 的 Version、CipherSuites、Random、以及 Extensions。
Version
:Client 給出自己能支援的、或者要使用的最高版本,比如 DTLS1.2。Server 收到這個資訊後,根據自己能支援的、或者要使用的版本回應,比如 DTLS1.0。最終以協商的版本也就是 DTLS1.0 為準。CipherSuites
:Client 給出自己能支援的加密套件 CipherSuites,Server 收到後選擇自己能支援的回應一個,比如 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014),加密套件確定了證書的類型、密鑰生成演算法、摘要演算法等。Random
:雙方的隨機數,參與到生成 MasterSecret。MasterSecret 會用來生成主密鑰,導出 SRTP 密鑰。詳見 [導出 SRTP 密鑰]Extensions
:Client 給出自己要使用的擴展協議,Server 可以回應自己支援的。比如 Client 雖然設置了 SessionTicket TLS 這個擴展,但是 Server 沒有回應,所以最終並不會使用這個擴展。
Cipher Suite
在 Hello 消息中加密套接字使用 IANA
中的註冊的名字。IANA 名字由 Protocol,Key Exchange Algorithm,Authentication Algorithm,Encryption Algorithm ,Hash Algorithm 的描述組成。例如,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 的含義如下:
Protocol
: Transport Layer Security (TLS)Key Exchange
: Elliptic Curve Diffie-Hellman Ephemeral (ECDHE)Authentication
: Rivest Shamir Adleman algorithm (RSA)Encryption
: Advanced Encryption Standard with 128bit key in Galois/Counter mode (AES 128 GCM)Hash
: Secure Hash Algorithm 256 (SHA256)
加密套接字在 ciphersuite.info 可以查到。在查到 IANA 名字的同時,也可以查到在 OpenSSL 中的名字。TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 在 OpenSSL 中的名字為 ECDHE-RSA-AES128-GCM-SHA256
Note: 關於 Authentication (認證)、KeyExchange (密鑰交換)、Encryption (加密)、MAC (Message Authentication Code) 消息摘要等,可以參考 RSA 密鑰協商。
Extension
DTLS 的擴展協議,是在 ClientHello 和 ServerHello 的 Extensions 資訊中指定的,所有的 TLS 擴展參考 TLS Extensions。下面列出幾個 WebRTC 用到的擴展:
use_srtp
: DTLS 握手完成後 (Finished),使用 SRTP 傳輸數據,DTLS 生成 SRTP 的密鑰。 RFC5764。ClientHello 中的擴展資訊定義了 RFC5764 4.1.2. SRTP Protection Profiles 和 srtp_mki。
supported_groups
,原來的名字為 elliptic_curves,描述支援的 ECC 加密演算法,參考 RFC8422 5.1.1.Supported Elliptic Curves Extension,一般用的是 secp256r1。
signature_algorithms
,DTLS1.2 的擴展,指定使用的 Hash 和 Signature 演算法,參考 RFC5246 7.4.1.4.1. Signature Algorithms。DTLS1.0,RSA 用的是 md5sha1 摘要演算法,DSA 用的是 sha1 摘要演算法。
extended_master_secret
,擴展 MasterSecret 的生成方式,參考 RFC7627。在 KeyExchange 中,會加入一些常量來生成 MasterSecret。TLS 定義了擴展方式,如果用這個擴展,DTLS 的方式和 TLS 會有些不同。renegotiation_info
, 參考 RFC5746
除了這些擴展協議,和 SRTP 密鑰導出相關的還有:
RFC5705: Keying Material Exporters for Transport Layer Security (TLS),DTLS 如何從 MasterSecret 導出 Key,比如 SRTP 的 Key。
RFC5764: DTLS Extension to Establish Keys for the SRTP,DTLS 的 use_srtp 擴展的詳細規範,包括 ClientHello 擴展定義、Profile 定義、Key 的計算。
身份驗證 – Certificate
數字證書是由一些公認可信的證書頒發機構簽發的,不易偽造。數字證書可以用於接收者驗證對端的身份,接收者收到某個對端的證書時,會對簽名頒發機構的數字簽名進行檢查,一般來說,接收者事先就會預先安裝很多常用的簽名頒發機構的證書(含有公開密鑰),利用預先的公開密鑰可以對簽名進行驗證。
Server 端通過 Hello 消息,協商交換密鑰的方法後,將 Server 證書發送給 Client,用於 Client 對 Server 的身份進行校驗。Server 發送的證書必須適用於協商的 KeyExchange 使用的加密套接字,以及 Hello 消息擴展中描述的 Hash/Signature 演算法對。
在 WebRTC 中,通訊的雙方通常將無法獲得由知名根證書頒發機構 (CA) 簽名的身份驗證證書,自簽名證書通常是唯一的選擇。RFC4572 定義一種機制,通過在 SDP
中增加自簽名證書的安全哈希,稱為 “證書指紋”,在保證 SDP 安全傳輸的前提下,如果提供的證書的指紋與 SDP
中的指紋匹配,則可以信任自簽名證書。在實際的應用場景中,SDP 在安全的信令通道 (https) 完成交換的,SDP 的安全完整是可以做到的。這樣在 DTLS 協商過程中,可以使用證書的指紋,完成通訊雙方的身份校驗。證書指紋在 SDP 中的描述如下,參考 SDP-Anatomy 中 DTLS 參數
a=fingerprint:sha-256 49:66:12:17:0D:1C:91:AE:57:4C:C6:36:DD:D5:97:D2:7D:62:C9:9A:7F:B9:A3:F4:70:03:E7:43:91:73:23:5E
密鑰交換 – KeyExchange
ServerKeyExchange 用來將 Server 端使用的公鑰,發送給 Client 端。分為兩種情況:
RSA
演算法:如果服務端使用的是 RSA 演算法,可以不發送這個消息,因為 RSA 演算法使用的公鑰已經在 Certificate 中描述。DH
演算法,是根據對方的公鑰和自己私鑰計算共享密鑰。因為 Client 和 Server 都只知道自己的私鑰,和對方的公鑰;而他們的私鑰都不同,根據特殊的數學特性,他們能計算出同樣的共享密鑰。關於 DH 演算法如何計算出共享密鑰,參考 DH 演算法。
ClientKeyExchange 用來將 Client 使用的公鑰,發送給 Server 端。
RSA
演算法:如果密鑰協商使用的 RSA 演算法,發送使用 server 端 RSA 公鑰,對 premaster secret 加密發送給 server 端。DH
演算法:如果密鑰協商使用的 DH 演算法,並且在證書中沒有描述,在將客戶端使用的 DH 演算法公鑰發送給 Server 端,以便計算出共享密鑰。
KeyExchange 的結果是,Client 和 Server 獲取到了 RSA Key, 或通過 DH 演算法計算出共享密鑰。詳見 [導出 SRTP 密鑰] 的過程
證書驗證 – CertificateVerify
使用 ClientRequest 中描述的 Hash/Signature 演算法,對收到和發送的 HandShake 消息簽名發送個 Server。Server 端對簽名進行校驗。
加密驗證 – Finished
當 Server 和 Client 完成對稱密鑰的交換後,通過 ChangeCipherSpec
通知對端進入加密階段,epoch 加 1。
隨後 Client 使用交換的密鑰,對 “client finished” 加密,使用 Finished 消息,發送給服務端。Server 使用交換的密鑰,對 “server finished” 進行加密發送給客戶端。一旦驗證了 finished 消息後,就可以正常通訊了。
導出 SRTP 密鑰
上面介紹了 DTLS 的過程,以下通過結合上面例子給出的實際數據,詳細說明 SRTP 密鑰的導出步驟。
協商後的加密演算法
加密套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
橢圓曲線演算法為:secp256r1,橢圓曲線的點壓縮演算法為:uncompressed。
橢圓曲線演算法的基礎知識的介紹在 ECC 橢圓曲線加密演算法 – ECDH,ECDHE,ECDSA ,由文檔中我們可以知道,確定橢圓曲線加密演算法有如下參數:
- 素數 p,用於確定有限域的範圍
- 橢圓曲線方程中的 a,b 參數
- 用於生成子群的的基點 G
- 子群的階 n
- 子群的輔助因子 h
定義為六元組(p,a,b,G,n,h)
通過在 SECG-SEC2 2.4.2 Recommended Parameters secp256r1 中可以查到 secp256r1 對應的參數如下:
secp256r1使用的參數如下:
使用的素數p:
p=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
=2224(232−1)+2192+296−1
橢圓曲線E:y^2=x^3+ax+b的參數定義如下:
a=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
b=5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
基點G的非壓縮格式:
G=046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
有限域的階n:
n=FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
輔助因子h:
h=01
通過 KeyExchange 交換橢圓曲線演算法公鑰
ECDH Server Parameter
Pubkey:04b0ce3c5f2c4a9fbe7c2257c1328438f3378f74e9f528b6e27a00b44eee4c19e5e6b2cb6cab09f796bcf8c05102b2a4bcdc753d91cc4f431f558c845a1ba6f1ce
記為 Spk
ECDH Client Paramter
PubKey: 0454e8fbef1503109d619c39be0ccaf89efa3c3962300476465cbc66b15152cd8a900c45d506420f0123e65d8fbb70cb60b497893f81c5c2a0ef2f4bc2da996d9e
記為 Cpk
根據 ECDHE 演算法計算共享密鑰 S(pre-master-secret)
假設 Server 的使用的橢圓曲線私鑰為 Ds, Client 使用的 Dc,計算共享密鑰的如下,參考 ECC 橢圓曲線加密演算法 – ECDH,ECDHE,ECDSA
S = Ds * Cpk = Dc * Spk
這個共享密鑰 S
就是我們在 RFC 文檔中看到的 pre-master-secret
計算 master secret
計算 master secret
過程如下,可參考 Computing the Master Secret
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)[0..47];
計算出來的 master_secret
為 48 Bytes,其中 ClientHello.random
和 ServerHello.random
在 Hello 消息中給出。PRF
是偽隨機數函數 (pseudorandom function),在協商的加密套件中給出。
使用 master_secrete 導出 SRTP 加密參數位元組序列
使用 RFC5705 4. Exporter Definition 給出的計算方式,使用參數 master_secret
, client_random
,server_random
計算位元組序列:
key_block = PRF(master_secret, "EXTRACTOR-dtls_srtp", client_random + server_random)[length]
在 DTLS-SRTP 4.2. Key Derivation 中描述了需要的位元組序列長度。
2 * (SRTPSecurityParams.master_key_len + SRTPSecurityParams.master_salt_len) bytes of data
master_key_len 和 master_salt_len 的值,在 user_srtp
描述的 profile 中定義。我們的實例中使用的 profile 為 SRTP_AES128_CM_HMAC_SHA1_80
,對應的 profile 配置為:
SRTP_AES128_CM_HMAC_SHA1_80
cipher: AES_128_CM
cipher_key_length: 128
cipher_salt_length: 112
maximum_lifetime: 2^31
auth_function: HMAC-SHA1
auth_key_length: 160
auth_tag_length: 80
也就是我們需要 (128/8+112/8)*2 = 60 bytes
位元組序列。
導出 SRTP 密鑰
計算出 SRTP 加密參數位元組序列,在 DTLS-SRTP 4.2. Key Derivation 描述了位元組序列的含義:
client_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; // 128 bits
server_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; // 128 bits
client_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; // 112 bits
server_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; // 112 bits
至此我們得到了,Client 和 Server 使用的 SRTP 加密參數:master_key 和 master_salt.
DTLS 超時重傳
DTLS 是基於 UDP 的,不可避免會出現丟包,需要重傳。如果處理不當,會導致整個通訊雙方無法建立會話,通話失敗。RFC6347 4.2.4 給出了超時和重傳機制。
在處理重傳時,以下幾點需要注意:
- 在 DTLS 協議中,為了解決丟包和重傳問題,新增了 message_seq. 在發送 DTLS 重傳消息時,一定要更新其中的
message_seq
,這樣對端將把包識別是一個重傳包,響應正確消息。否則,會默默丟棄這些包,不進行響應。 - 當 server 端收到 client 的 FINISHED 消息,並發送 FINISHED 消息給 client,更新 server 狀態為協商完成,開始發送 SRTP 數據。此時發送給 client 的 FINISHED 消息,出現丟包。client 收到 SRTP 數據後丟棄。同時,再次發送 FINISHED 消息到 server,server 要正確響應。否則,會導致 DTLS 協商完成的假象,通話失敗。
- 使用 openssl 1.1.1 之前版本,無法設置 DTLS 超時重傳時間,可以超時重傳機制不可用,大家開始轉向使用 boringssl。openssl 1.1.1 開始版本已經支援設置 DTLS 超時重傳,達到和 boringssl 同樣的效果。參考 DTLS_set_timer_cb
OpenSSL 的 DTLS 功能
DTLS 是一個龐大的協議體系,其中包括了各種加密,簽名,證書,壓縮等多種演算法。大多數項目是基於 OpenSSL 或 BoringSSL 實現的 DTLS 功能。在實際項目使用 OpenSSL 的 DTLS 功能,與協商有關的介面總結如下。
- X509_digest,計算證書 fingerprint,用在 SDP 協商中的
fingerprint
屬性。 - SSL_CTX_set_cipher_list,設置使用的加密套件,通過設置演算法的描述,影響 Hello 消息中的 cipher list。
- SSL_CTX_set1_sigalgs_list 設置簽名演算法。通過設置簽名演算法的描述,影響 hello 消息中
signature_algorithms
擴展。signature_algorithms
對 DTLS 的Hello 消息
,KeyExchange
,CerficateVerify
消息。signature_algorithms
設置不正確,會出現 internal error,不容易定位問題。 - SSL_CTX_set_verify 設置是否校驗對端的證書。由於在 RTC 中大多數據情況下使用自簽證書,所以對證書的校驗,已校驗身份是需要的。
- SSL_CTX_set_tlsext_use_srtp 設置
srtp
擴展。srtp 擴展中的 profile,影響 srtp 加密時使用密鑰的協商和提取。 - SSL_set_options 使用
SSL_OP_NO_QUERY_MTU
和 [SSL_set_mtu] 設置 fragment 的大小。默認 OpenSSL 使用 fragment 較小。通過上面兩個介面,設置適合網路情況的 fragment。 - DTLS_set_timer_cb,設置超時重傳的 Callback,由 callback 設置更合理的超時重傳時間。
在開源項目 SRS 中已經支援了 WebRTC 的基礎協議,對 DTLS 協議感興趣的同學,可以基於 SRS 快速搭建本機環境,通過調試,進一步加深對 DTLS 的理解。
總結
本文通過 WebRTC 中 SRTP 密鑰的協商過程,來說明 DTLS 在 WebRTC 中的應用。DTLS 協議設計的各個加密演算法的知識較多,加上 TLS 消息的在各種應用場景中的擴展,難免有理解和認知不到的地方,還需要進一步深入探索。
參考文獻
- TLS 1.2
- DTLS 1.2
- TLS Session Hash Extension
- TCP-Based Media Transport in the Session Description Protocol
- TLS Extension
- SRTP Extension for DTLS
- OpenSSL Man
- ECC 橢圓曲線加密演算法 – 介紹
- ECC 橢圓曲線加密演算法 – 有限域和離散對數
- ECC 橢圓曲線加密演算法 – ECDH、ECDHE 和 ECDSA
「影片雲技術」你最值得關注的音影片技術公眾號,每周推送來自阿里雲一線的實踐技術文章,在這裡與音影片領域一流工程師交流切磋。公眾號後台回復【技術】可加入阿里雲影片雲技術交流群,和作者一起探討音影片技術,獲取更多行業最新資訊。