DTLS(Datagram Transport Layer Security) 是基于 UDP 场景下数据包可能丢失或重新排序的现实情况下,为 UDP 定制和改进的 TLS 协议。在 WebRTC 中使用 DTLS 的地方包括两部分: 协商和管理 SRTP 密钥和为 DataChannel 提供加密通道。

webrtc原理(WebRTC详解传输安全机制)(1)

本文结合实际数据包分析 WebRTC 使用 DTLS 进行 SRTP 密钥协商的流程。并对在实际项目中使用 DTLS 遇到的问题进行总结。

01 DTLS 协议简介

在分析 DTLS 在 WebRTC 中的应用之前,先介绍下 DTLS 协议的基本原理。

DTLS 协议由两层组成: Record 协议 和 Handshake 协议。

1. Record 协议: 使用对称密钥对传输数据进行加密,并使用 HMAC 对数据进行完整性校验,实现了数据的安全传输。

2. Handshake 协议:使用非对称加密算法,完成 Record 协议使用的对称密钥的协商。

1. HandShake

TLS 握手协议流程如下,参考 RFC5246。

webrtc原理(WebRTC详解传输安全机制)(2)

DTLS 握手协议流程如下,参考 RFC6347。

webrtc原理(WebRTC详解传输安全机制)(3)

webrtc原理(WebRTC详解传输安全机制)(4)

TLS 和 DTLS 的握手过程基本上是一致的,差别以及特别说明如下:

RecordLayer 协议是和 DTLS 传输相关的协议,UDP 之上是 RecordLayer,RecordLayer 之上是 Handshake 或 ChangeCipherSpec 或 ApplicationData。RecordLayer 协议定义参考 RFC4347,实际上有三种 RecordLayer 的包:

没有明确的字段说明是哪种消息,不过可以根据上下文以及内容判断。比如 ChangeCipherSpec 是可以通过类型,它肯定是一个 Plaintext。除了 Finished 的其他握手,一般都是 Plaintext。

02 SRTP 密钥协商

1. 角色协商

在 DTLS 协议,通信的双方有 Client 和 Server 之分。在 WebRTC 中 DTLS 协商的身份是在 SDP 中描述的。描述如下,参考 SDP-Anatomy 中 DTLS 参数。

a=setup:active

setup 属性在 RFC4145,

setup:active,作为 client,主动发起协商;

setup:passive, 作为 sever,等待发起协商;

setup:actpass, 作为 client,主动发起协商。作为 server,等待发起协商。

2. 算法协商-Hello 消息

ClienHello 和 ServerHello 协商 DTLS 的 Version、CipherSuites、Random、以及 Extensions。

webrtc原理(WebRTC详解传输安全机制)(5)

webrtc原理(WebRTC详解传输安全机制)(6)

Cipher Suite

webrtc原理(WebRTC详解传输安全机制)(7)

在 Hello 消息中加密套接字使用 IANA 中的注册的名字。IANA 名字由 Protocol,Key Exchange Algorithm,Authentication Algorithm,Encryption Algorithm ,Hash Algorithm 的描述组成。

例如,TLS_ECDHE_RSA_WITH_AES_128_GCM_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 用到的扩展:

webrtc原理(WebRTC详解传输安全机制)(8)

supported_groups,原来的名字为 elliptic_curves,描述支持的 ECC 加密算法,参考 RFC8422 5.1.1.Supported Elliptic Curves Extension,一般用的是 secp256r1。

webrtc原理(WebRTC详解传输安全机制)(9)

webrtc原理(WebRTC详解传输安全机制)(10)

除了这些扩展协议,和 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 的计算。

3. 身份验证-Certificate

webrtc原理(WebRTC详解传输安全机制)(11)

数字证书是由一些公认可信的证书颁发机构签发的,不易伪造。数字证书可以用于接收者验证对端的身份,接收者收到某个对端的证书时,会对签名颁发机构的数字签名进行检查,一般来说,接收者事先就会预先安装很多常用的签名颁发机构的证书(含有公开密钥),利用预先的公开密钥可以对签名进行验证。

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

4. 密钥交换-KeyExchange

ServerKeyExchange 用来将 Server 端使用的公钥,发送给 Client 端。分为两种情况:

1. RSA 算法: 如果服务端使用的是 RSA 算法,可以不发送这个消息,因为 RSA 算法使用的公钥已经在 Certificate 中描述。

2. DH 算法,是根据对方的公钥和自己私钥计算共享密钥。因为 Client 和 Server 都只知道自己的私钥,和对方的公钥;而他们的私钥都不同,根据特殊的数学特性,他们能计算出同样的共享密钥。关于 DH 算法如何计算出共享密钥,参考 DH算法。

webrtc原理(WebRTC详解传输安全机制)(12)

ClientKeyExchange 用来将 Client 使用的公钥,发送给 Server 端。

1. RSA 算法:如果密钥协商使用的 RSA 算法,发送使用 server 端 RSA 公钥,对 premaster secret 加密发送给 server 端。

2. DH 算法:如果密钥协商使用的 DH 算法,并且在证书中没有描述,在将客户端使用的 DH 算法公钥发送给 Server 端,以便计算出共享密钥。

webrtc原理(WebRTC详解传输安全机制)(13)

KeyExchange 的结果是,Client 和 Server 获取到了 RSA Key, 或通过 DH 算法计算出共享密钥。详见 [导出 SRTP 密钥] 的过程。

5. 证书验证-CertificateVerify

客户端使用 ClientRequest 中描述的 Hash/Signature 算法,对收到和发送的 HandShake 消息签名发送给 Server。Server 端对签名进行校验。

webrtc原理(WebRTC详解传输安全机制)(14)

6. 加密验证-Finished

当 Server 和 Client 完成对称密钥的交换后,通过 ChangeCipherSpec 通知对端进入加密阶段,epoch 加 1。

随后 Client 使用交换的密钥,对 "client finished" 加密,使用 Finished 消息,发送给服务端。Server 使用交换的密钥,对 "server finished" 进行加密发送给客户端。一旦验证了 finished 消息后,就可以正常通信了。

webrtc原理(WebRTC详解传输安全机制)(15)

03 导出 SRTP 密钥

上面介绍了 DTLS 的过程,以下通过结合上面例子给出的实际数据,详细说明 SRTP 密钥的导出步骤。

1. 协商后的加密算法

加密套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xc02f)

椭圆曲线算法为:secp256r1,椭圆曲线的点压缩算法为:uncompressed。

椭圆曲线算法的基础知识的介绍在 ECC椭圆曲线加密算法-ECDH,ECDHE,ECDSA,由文档中我们可以知道,确定椭圆曲线加密算法有如下参数:

定义为六元组(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

2. 通过 KeyExchange 交换椭圆曲线算法公钥

ECDH Server Parameter

Pubkey:04b0ce3c5f2c4a9fbe7c2257c1328438f3378f74e9f528b6e27a00b44eee4c19e5e6b2cb6cab09f796bcf8c05102b2a4bcdc753d91cc4f431f558c845a1ba6f1ce

记为 Spk

ECDH Client Paramter

PubKey: 0454e8fbef1503109d619c39be0ccaf89efa3c3962300476465cbc66b15152cd8a900c45d506420f0123e65d8fbb70cb60b497893f81c5c2a0ef2f4bc2da996d9e

记为 Cpk

3. 根据 ECDHE 算法计算共享密钥 S(pre-master-secret)

假设 Server 的使用的椭圆曲线私钥为 Ds, Client 使用的 Dc,计算共享密钥的如下 , 参考 ECC 椭圆曲线加密算法-ECDH,ECDHE,ECDSA

S = Ds * Cpk = Dc * Spk

这个共享密钥 S 就是我们在 RFC 文档中看到的 pre-master-secret

4. 计算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),在协商的加密套件中给出。

5. 使用 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 字节序列。

6. 导出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。

04 DTLS 超时重传

DTLS 是基于 UDP 的,不可避免会出现丢包,需要重传。如果处理不当,会导致整个通信双方无法建立会话,通话失败。RFC6347 4.2.4 给出了超时和重传机制。

在处理重传时,以下几点需要注意:

1. 在 DTLS 协议中,为了解决丢包和重传问题,新增了 message_seq。 在发送 DTLS 重传消息时,一定要更新其中的 message_seq,这样对端将把包识别是一个重传包,响应正确消息。否则,会默默丢弃这些包,不进行响应。

2. 当 server 端收到 client 的 FINISHED 消息,并发送 FINISHED 消息给 client,更新 server 状态为协商完成,开始发送 SRTP 数据。此时发送给 client 的 FINISHED 消息,出现丢包。client 收到 SRTP 数据后丢弃。同时,再次发送 FINISHED 消息到 server,server 要正确响应。否则,会导致 DTLS 协商完成的假象,通话失败。

3. 使用 openssl 1.1.1 之前版本,无法设置 DTLS 超时重传时间,可以超时重传机制不可用,大家开始转向使用 boringssl。openssl 1.1.1 开始版本已经支持设置 DTLS 超时重传,达到和 boringssl 同样的效果。参考 DTLS_set_timer_cb。

05 OpenSSL 的 DTLS 功能

DTLS 是一个庞大的协议体系,其中包括了各种加密,签名,证书,压缩等多种算法。大多数项目是基于 OpenSSL 或 BoringSSL 实现的 DTLS 功能。在实际项目使用 OpenSSL 的 DTLS 功能,与协商有关的接口总结如下。

1. X509_digest,计算证书 fingerprint,用在 SDP 协商中的 fingerprint 属性。

2. SSL_CTX_set_cipher_list,设置使用的加密套件,通过设置算法的描述,影响 Hello 消息中的 cipher list。

3. SSL_CTX_set1_sigalgs_list 设置签名算法。通过设置签名算法的描述,影响 hello 消息中 signature_algorithms 扩展。signature_algorithms 对 DTLS 的 Hello消息,KeyExchange,CerficateVerify消息。signature_algorithms 设置不正确,会出现 internal error,不容易定位问题。

4. SSL_CTX_set_verify 设置是否校验对端的证书。由于在 RTC 中大多数据情况下使用自签证书,所以对证书的校验,已校验身份是需要的。

5. SSL_CTX_set_tlsext_use_srtp 设置 srtp 扩展。srtp 扩展中的 profile,影响 srtp 加密时使用密钥的协商和提取。

6. SSL_set_options 使用 SSL_OP_NO_QUERY_MTU 和 [SSL_set_mtu] 设置 fragment 的大小。默认 OpenSSL 使用 fragment 较小。通过上面两个接口,设置适合网络情况的 fragment。

7. DTLS_set_timer_cb,设置超时重传的 Callback,由 callback 设置更合理的超时重传时间。

在开源项目 SRS(https://github.com/ossrs/srs) 中已经支持了 WebRTC 的基础协议,对 DTLS 协议感兴趣的同学,可以基于 SRS 快速搭建本机环境,通过调试,进一步加深对 DTLS 的理解。

06 总结

本文通过 WebRTC 中 SRTP 密钥的协商过程,来说明 DTLS 在 WebRTC 中的应用。DTLS 协议设计的各个加密算法的知识较多,加上 TLS 消息的在各种应用场景中的扩展,难免有理解和认知不到的地方,还需要进一步深入探索。

参考文献
  1. TLS 1.2
  2. DTLS 1.2
  3. TLS Session Hash Extension
  4. TCP-Based Media Transport in the Session Description Protocol
  5. TLS Extension
  6. SRTP Extension for DTLS
  7. OpenSSL Man
  8. ECC椭圆曲线加密算法-介绍
  9. ECC椭圆曲线加密算法-有限域和离散对数
  10. ECC椭圆曲线加密算法-ECDH、ECDHE和ECDSA

END

作者|进学

灵云快智_专注实时音视频智能硬件场景解决方案,智能手表,智能机器人,智慧社区,企业通信

,