######################## TLS 协议 ######################## .. include:: ../links.ref .. include:: ../tags.ref .. include:: ../abbrs.ref ============ ========================== **Abstract** TLS protocol **Authors** Walter Fan **Status** v1.0 **Updated** |date| ============ ========================== .. contents:: :local: 概述 ======================= TLS (Transport Layer Security) 是一种广泛使用的安全协议,用于在两个通信方之间提供隐私和数据完整性保护。TLS 是 SSL (Secure Sockets Layer) 的后继者,目前已经发展到 TLS 1.3 版本。 在 WebRTC 中,TLS 及其变体 DTLS (Datagram TLS) 扮演着至关重要的角色: * **DTLS** 用于保护媒体传输通道,并为 SRTP 提供密钥协商 * **TLS** 用于保护信令通道(如 WebSocket over TLS) * **DTLS-SRTP** 是 WebRTC 强制要求的媒体加密方案 TLS 版本演进 ----------------------- .. list-table:: TLS 版本历史 :header-rows: 1 :widths: 15 15 40 30 * - 版本 - 年份 - 主要特点 - 状态 * - SSL 3.0 - 1996 - SSL 的最后版本 - 已废弃 (RFC 7568) * - TLS 1.0 - 1999 - RFC 2246,基于 SSL 3.0 - 已废弃 (RFC 8996) * - TLS 1.1 - 2006 - RFC 4346,修复 CBC 攻击 - 已废弃 (RFC 8996) * - TLS 1.2 - 2008 - RFC 5246,支持 AEAD,灵活的密码套件 - 广泛使用 * - TLS 1.3 - 2018 - RFC 8446,1-RTT 握手,移除弱密码 - 推荐使用 TLS 1.2 握手协议 ======================= TLS 1.2 的握手过程需要 2 个 RTT (Round-Trip Time) 才能完成,建立安全连接后才能传输应用数据。 完整握手流程 ----------------------- .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello Server->>Client: ServerHello Certificate* ServerKeyExchange* CertificateRequest* ServerHelloDone Client->>Server: Certificate* ClientKeyExchange CertificateVerify* ChangeCipherSpec Finished Server->>Client: ChangeCipherSpec Finished Note over Client,Server: Application Data 各步骤详解: **1. ClientHello** 客户端发起握手,包含以下信息: * 支持的 TLS 版本 * 客户端随机数 (Client Random, 32 字节) * Session ID(用于会话恢复) * 支持的密码套件列表 (Cipher Suites) * 支持的压缩方法 * 扩展 (Extensions),如 SNI、supported_groups、signature_algorithms **2. ServerHello** 服务器响应,选择参数: * 选定的 TLS 版本 * 服务器随机数 (Server Random, 32 字节) * Session ID * 选定的密码套件 * 选定的压缩方法 **3. Certificate** 服务器发送其 X.509 证书链,客户端用于验证服务器身份。 **4. ServerKeyExchange** 当使用 DHE 或 ECDHE 密钥交换时,服务器发送临时公钥参数。 **5. CertificateRequest (可选)** 服务器请求客户端证书,用于双向认证 (mutual TLS)。 **6. ServerHelloDone** 标志服务器 Hello 阶段结束。 **7. ClientKeyExchange** 客户端发送密钥交换参数。对于 RSA,发送用服务器公钥加密的 Pre-Master Secret;对于 ECDHE,发送客户端的临时公钥。 **8. ChangeCipherSpec** 通知对方后续消息将使用协商好的密码套件加密。 **9. Finished** 包含所有握手消息的哈希值,用于验证握手过程的完整性。 密钥推导 ----------------------- TLS 1.2 的密钥推导过程: .. mermaid:: flowchart TB A[Pre-Master Secret] --> B["Master Secret = PRF(pre_master_secret, master secret, ClientHello.random + ServerHello.random)"] B --> C["Key Block = PRF(master_secret, key expansion, ServerHello.random + ClientHello.random)"] C --> D[client_write_MAC_key] C --> E[server_write_MAC_key] C --> F[client_write_key] C --> G[server_write_key] C --> H[client_write_IV] C --> I[server_write_IV] 会话恢复 ----------------------- TLS 1.2 支持两种会话恢复机制,避免完整握手的开销: **Session ID 恢复** .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello with Session ID Server->>Client: ServerHello same Session ID ChangeCipherSpec Finished Client->>Server: ChangeCipherSpec Finished Note over Client,Server: Application Data **Session Ticket 恢复 (RFC 5077)** 服务器将会话状态加密后作为 ticket 发送给客户端,客户端在后续连接中携带该 ticket,服务器无需维护会话状态。 .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello empty SessionTicket extension Server->>Client: ServerHello empty SessionTicket Certificate* ServerKeyExchange* CertificateRequest* ServerHelloDone Client->>Server: Certificate* ClientKeyExchange CertificateVerify* ChangeCipherSpec Finished Server->>Client: NewSessionTicket ChangeCipherSpec Finished Note over Client,Server: Application Data TLS 1.3 改进 ======================= TLS 1.3 (RFC 8446) 是 TLS 协议的重大升级,在安全性和性能方面都有显著改进。 1-RTT 握手 ----------------------- TLS 1.3 将握手缩短为 1 个 RTT: .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello + key_share + signature_algorithms + psk_key_exchange_modes + pre_shared_key Server->>Client: ServerHello + key_share + pre_shared_key EncryptedExtensions CertificateRequest* Certificate* CertificateVerify* Finished Client->>Server: Certificate* CertificateVerify* Finished Note over Client,Server: Application Data 关键改进:客户端在 ClientHello 中就发送了 key_share(密钥交换参数),服务器可以立即计算共享密钥,无需额外的往返。 0-RTT 恢复 (Early Data) ----------------------- TLS 1.3 支持 0-RTT 恢复,客户端可以在第一个消息中就发送应用数据: .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello + early_data + key_share + psk_key_exchange_modes + pre_shared_key Application Data Server->>Client: ServerHello + pre_shared_key EncryptedExtensions + early_data* Finished Client->>Server: EndOfEarlyData Finished Note over Client,Server: Application Data .. warning:: 0-RTT 数据不具备前向安全性 (forward secrecy),且容易受到重放攻击 (replay attack)。应仅用于幂等操作。 移除的弱密码和特性 ----------------------- TLS 1.3 移除了以下不安全的特性: * **RSA 密钥交换**: 不提供前向安全性 * **CBC 模式密码**: 容易受到 padding oracle 攻击 * **RC4 流密码**: 已被证明不安全 * **SHA-1 哈希**: 已被碰撞攻击 * **静态 DH 密钥交换**: 不提供前向安全性 * **压缩**: 容易受到 CRIME 攻击 * **重协商 (Renegotiation)**: 安全隐患 TLS 1.3 仅保留以下密码套件: .. code-block:: TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_CCM_SHA256 TLS_AES_128_CCM_8_SHA256 DTLS — WebRTC 的安全基石 ======================== 为什么使用 DTLS 而不是 TLS ----------------------------------------- WebRTC 的媒体传输基于 UDP,而 TLS 设计用于可靠的 TCP 传输。DTLS (Datagram TLS) 是 TLS 的 UDP 适配版本,解决了以下问题: * **数据包丢失**: DTLS 实现了自己的重传机制,不依赖 TCP 的可靠传输 * **数据包乱序**: DTLS 使用显式的序列号来处理乱序 * **数据包重复**: DTLS 可以检测和丢弃重复的数据包 * **无连接**: UDP 是无连接的,DTLS 在其上建立安全的"连接" DTLS 版本对应关系: .. list-table:: :header-rows: 1 * - DTLS 版本 - 对应 TLS 版本 - RFC * - DTLS 1.0 - TLS 1.1 - RFC 4347 * - DTLS 1.2 - TLS 1.2 - RFC 6347 * - DTLS 1.3 - TLS 1.3 - RFC 9147 DTLS 握手 ----------------------------------------- DTLS 握手与 TLS 类似,但增加了以下机制来适应 UDP: **HelloVerifyRequest (Cookie 交换)** 防止 DoS 攻击,服务器在正式握手前要求客户端回显一个 cookie: .. mermaid:: sequenceDiagram participant Client participant Server Client->>Server: ClientHello Server->>Client: HelloVerifyRequest with cookie Client->>Server: ClientHello with cookie Server->>Client: ServerHello Certificate* ServerKeyExchange* CertificateRequest* ServerHelloDone Client->>Server: Certificate* ClientKeyExchange CertificateVerify* ChangeCipherSpec Finished Server->>Client: ChangeCipherSpec Finished Note over Client,Server: Application Data **重传机制** DTLS 使用定时器驱动的重传机制。每个握手消息都有一个 message_seq 编号,接收方可以检测丢失和乱序: .. code-block:: struct { ContentType type; ProtocolVersion version; uint16 epoch; // DTLS 特有:密钥变更计数 uint48 sequence_number; // DTLS 特有:显式序列号 uint16 length; opaque fragment[DTLSPlaintext.length]; } DTLSPlaintext; **消息分片** DTLS 握手消息可能超过 UDP MTU,因此支持分片: .. code-block:: struct { HandshakeType msg_type; uint24 length; uint16 message_seq; // DTLS 特有 uint24 fragment_offset; // DTLS 特有 uint24 fragment_length; // DTLS 特有 select (HandshakeType) { ... } body; } Handshake; DTLS-SRTP 密钥推导 ----------------------------------------- DTLS-SRTP (RFC 5764) 是 WebRTC 中用于保护 RTP/RTCP 媒体流的机制。DTLS 握手完成后,双方使用协商的密钥材料推导 SRTP 密钥。 密钥推导过程: .. mermaid:: flowchart TB A[DTLS 握手完成] --> B["TLS-Exporter EXTRACTOR-dtls_srtp
client_random + server_random"] B --> C[分割密钥材料] C --> D[client_write_SRTP_master_key] C --> E[server_write_SRTP_master_key] C --> F[client_write_SRTP_master_salt] C --> G[server_write_SRTP_master_salt] SRTP protection profile 在 DTLS 握手中通过 ``use_srtp`` 扩展协商: .. code-block:: 常用的 SRTP protection profiles: SRTP_AES128_CM_HMAC_SHA1_80 (0x0001) - 最常用 SRTP_AES128_CM_HMAC_SHA1_32 (0x0002) SRTP_AEAD_AES_128_GCM (0x0007) SRTP_AEAD_AES_256_GCM (0x0008) SDP 中的 DTLS 指纹 ----------------------------------------- WebRTC 使用 SDP 中的 ``a=fingerprint`` 属性来验证 DTLS 证书的真实性。信令通道(通常通过 HTTPS/WSS 保护)传递证书指纹,DTLS 握手时验证对端证书的指纹是否匹配。 .. code-block:: SDP 中的相关属性: a=fingerprint:sha-256 4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:... a=setup:actpass a=dtls-id:abc123 ``a=setup`` 属性指定 DTLS 角色: * ``actpass``: 可以作为客户端或服务器(通常在 offer 中使用) * ``active``: 作为 DTLS 客户端(发起握手) * ``passive``: 作为 DTLS 服务器(等待握手) 证书管理 ======================= WebRTC 中的自签名证书 ----------------------------------------- WebRTC 使用自签名证书 (self-signed certificate) 进行 DTLS 握手,不依赖传统的 CA (Certificate Authority) 体系。安全性通过信令通道传递的证书指纹来保证。 .. code-block:: javascript // 浏览器自动生成证书 const pc = new RTCPeerConnection(); // 或者手动生成证书 const certificate = await RTCPeerConnection.generateCertificate({ name: 'ECDSA', namedCurve: 'P-256' }); const pc = new RTCPeerConnection({ certificates: [certificate] }); 证书生命周期: 1. ``RTCPeerConnection`` 创建时生成(或使用预生成的证书) 2. 证书指纹通过 SDP offer/answer 交换 3. DTLS 握手时验证对端证书指纹 4. 连接关闭后证书可以丢弃 指纹验证流程 ----------------------------------------- .. mermaid:: sequenceDiagram participant Alice participant Sig as Signaling Server participant Bob Alice->>Sig: "SDP Offer a=fingerprint sha-256 AA:BB:..." Sig->>Bob: "SDP Offer a=fingerprint sha-256 AA:BB:..." Bob->>Sig: "SDP Answer a=fingerprint sha-256 CC:DD:..." Sig->>Alice: "SDP Answer a=fingerprint sha-256 CC:DD:..." Note over Alice,Bob: DTLS handshake P2P Note over Alice: "verify Bob fingerprint == CC:DD:..." Note over Bob: "verify Alice fingerprint == AA:BB:..." WebRTC 中的密码套件 ======================= WebRTC 实现通常支持以下密码套件: **密钥交换算法** * **ECDHE (Elliptic Curve Diffie-Hellman Ephemeral)**: 提供前向安全性,使用椭圆曲线(如 P-256、X25519) **对称加密算法** * **AES-128-GCM**: 高性能 AEAD 加密,硬件加速支持广泛 * **AES-256-GCM**: 更高安全级别的 AEAD 加密 * **ChaCha20-Poly1305**: 在没有 AES 硬件加速的平台上性能更好 **哈希算法** * **SHA-256**: 用于密钥推导和消息认证 * **SHA-384**: 用于更高安全级别的场景 常见的 DTLS 密码套件组合: .. code-block:: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 SRTP 密钥推导详解 ======================= DTLS 握手完成后,SRTP 密钥的推导过程如下: .. code-block:: 1. DTLS 握手协商 SRTP protection profile (通过 use_srtp 扩展) 2. 使用 TLS exporter 导出密钥材料 总长度 = 2 * (master_key_len + master_salt_len) 对于 SRTP_AES128_CM_HMAC_SHA1_80: master_key_len = 16 bytes master_salt_len = 14 bytes 总长度 = 2 * (16 + 14) = 60 bytes 3. 密钥材料分割: bytes 0-15: client_write_SRTP_master_key (16 bytes) bytes 16-31: server_write_SRTP_master_key (16 bytes) bytes 32-45: client_write_SRTP_master_salt (14 bytes) bytes 46-59: server_write_SRTP_master_salt (14 bytes) 4. SRTP 使用这些密钥加密/解密 RTP 和 RTCP 数据包 SRTP 数据包加密过程: .. mermaid:: flowchart TB subgraph orig [原始 RTP 数据包] H1[RTP Hdr] --> P1[Payload] end subgraph srtp [SRTP 加密后] H2[RTP Hdr] --> P2[Encrypted Payload] P2 --> MKI["SRTP MKI optional"] MKI --> TAG[Auth Tag] end orig --> srtp mTLS (双向 TLS) ======================= mTLS (mutual TLS) 要求客户端和服务器都提供证书进行身份验证。在 WebRTC 中,DTLS 握手本质上就是双向认证的——双方都发送自己的证书,并验证对方的证书指纹。 mTLS 的典型应用场景: * **WebRTC**: DTLS 握手中双方交换自签名证书 * **API 安全**: 微服务之间的安全通信 * **零信任网络**: 每个连接都需要双向认证 .. code-block:: 标准 TLS (单向认证): 客户端验证服务器证书 ✓ 服务器不验证客户端 ✗ mTLS (双向认证): 客户端验证服务器证书 ✓ 服务器验证客户端证书 ✓ 常见 TLS/DTLS 问题 ======================= 在 WebRTC 开发中,常见的 TLS/DTLS 相关问题包括: 证书错误 ----------------------------------------- * **指纹不匹配**: SDP 中的 fingerprint 与实际 DTLS 证书不一致,通常是因为 SDP 被修改或证书被重新生成 * **证书过期**: 自签名证书通常有较短的有效期 * **不支持的签名算法**: 双方支持的签名算法不兼容 DTLS 超时 ----------------------------------------- * **握手超时**: DTLS 握手在 UDP 上进行,丢包会导致重传和延迟。默认重传定时器从 1 秒开始,指数退避 * **ICE 与 DTLS 的时序**: DTLS 握手必须在 ICE 连接建立之后进行 * **防火墙阻止**: 某些防火墙可能阻止 DTLS 流量 版本不匹配 ----------------------------------------- * **DTLS 版本协商失败**: 双方支持的 DTLS 版本不兼容 * **密码套件不匹配**: 没有共同支持的密码套件 调试技巧 ----------------------------------------- .. code-block:: bash # 使用 openssl 测试 DTLS openssl s_client -dtls1_2 -connect server:4433 # 使用 Wireshark 过滤 DTLS 流量 # dtls # dtls.handshake.type == 1 (ClientHello) # dtls.handshake.type == 2 (ServerHello) # Chrome 浏览器查看 WebRTC 内部状态 # chrome://webrtc-internals/ SSL 握手失败的常见原因 ----------------------------------------- * 客户端使用了错误的日期或时间 * 浏览器的特定配置导致错误 * 连接被客户端侧的第三方拦截 * 客户端和服务器不支持相同的 SSL/TLS 版本 * 客户端和服务器使用不同的密码套件 * 客户端或服务器的证书无效 Alert 协议 ======================= TLS Alert 协议用于通知对方发生了错误或连接即将关闭: .. code-block:: enum { warning(1), fatal(2), (255) } AlertLevel; enum { close_notify(0), unexpected_message(10), bad_record_mac(20), decryption_failed_RESERVED(21), record_overflow(22), decompression_failure(30), handshake_failure(40), no_certificate_RESERVED(41), bad_certificate(42), unsupported_certificate(43), certificate_revoked(44), certificate_expired(45), certificate_unknown(46), illegal_parameter(47), unknown_ca(48), access_denied(49), decode_error(50), decrypt_error(51), export_restriction_RESERVED(60), protocol_version(70), insufficient_security(71), internal_error(80), user_canceled(90), no_renegotiation(100), unsupported_extension(110), (255) } AlertDescription; struct { AlertLevel level; AlertDescription description; } Alert; 在 WebRTC 中,常见的 DTLS alert 包括: * ``close_notify``: 正常关闭连接 * ``handshake_failure``: 握手失败,通常是密码套件不匹配 * ``bad_certificate``: 证书验证失败 * ``certificate_expired``: 证书已过期 参考资料 ========================= * `RFC 5246 - The Transport Layer Security (TLS) Protocol Version 1.2 `_ * `RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 `_ * `RFC 6347 - Datagram Transport Layer Security Version 1.2 `_ * `RFC 9147 - The Datagram Transport Layer Security (DTLS) Protocol Version 1.3 `_ * `RFC 5764 - Datagram Transport Layer Security (DTLS) Extension to Establish Keys for the Secure Real-time Transport Protocol (SRTP) `_ * `RFC 5077 - Transport Layer Security (TLS) Session Resumption without Server-Side State `_ * `RFC 5705 - Keying Material Exporters for Transport Layer Security (TLS) `_ * `High Performance Browser Networking - TLS `_