5

dtls1.2跟tls1.2的不同

 2 years ago
source link: http://suntus.github.io/2020/11/11/dtls12%E8%B7%9Ftls12%E7%9A%84%E4%B8%8D%E5%90%8C/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

dtls1.2跟tls1.2的不同

发表于

2020-11-11 18:50

|

更新于 2021-04-29 17:15

UDP不可靠,这个引起两个问题

  1. tls是按顺序加解密的,两端维持有一个seq number,乱了的话,就解密不出来了;这个seq在tls中不是随数据包发送的,而是在两端各自维护。还有种情况是使用流加密,流加密的状态也是在两端维护的。这里导致记录层数据包之间是有隐含关联的。
  2. tls握手是以消息已经排好顺序为前提的,如果乱了就会认为握手不正确。比如该server发送 ServerHello,直接发送了 Certificate, client就会认为是 unexpected message,直接终止连接。

2.1. 针对记录层数据包之间的关联性

  1. DTLS不使用流加密
  2. DTLS将seq number放到每个数据包,随数据包一起发送,这样对端就能直接取出seq来解密

2.2. 针对握手阶段消息的顺序

  1. 数据包可能丢失——->DTLS加上了握手消息重传机制
  2. 数据包可能重新排序——->DTLS给握手过程中的每个消息都加上seq number,如果是期望的下一个消息就处理,如果不是就先缓存
  3. 握手数据包大小可能超过IP数据包分片大小(通常<1500)----->DTLS允许一个握手数据包分成几个记录层数据包,保证每个记录层数据包都在PMTU之内。记录层数据包不会分成几个IP包;但几个记录层数据包可以在一个IP包中。

2.3. 重放检测

还有个TLS没有的问题,就是DTLS可能会收到重复的数据包,DTLS提供了一种可选的重放检查机制”bitmap”来检查重放,简单来说就是一个类似滑动有效窗口一样的范围,左侧是按顺序收到的最新的一个有效消息对应的seq number,右侧是DTLS认为有效的seq number,落在其中的消息都认为有效,都可以解密,落在窗口右侧的都可以解密校验MAC,校验通过就向右滑动窗口。

2.4. 无状态检查

这个其实DTLS和TLS(1.3)都有,但DTLS是必须的。

UDP没有连接,也没有三次握手,导致应用可以伪造数据包一直发送ClientHello,如果server每次收到ClientHello都建立握手状态,然后返回ServerHello,会导致server端内存很快耗尽。DTLS解决办法是收到 ClientHello 后,返回一个 HelloRetryRequest 的消息,该消息中带有一段”cookie”,其中存放 Cookie = HMAC(Secret, Client-IP, Client-Parameters),这是对client IP和加密参数的一个校验值,Secret 只有server才知道,这样就能方式client伪造。然后client再发送一个带上cookie的 ClientHello,server会再次验证该cookie是否是同一个client发来的,如果不是,server就拒绝连接。在验证第二个ClientHello有效之前,server不会保存握手状态,这样就可以防止client发起DDoS攻击。

3. 超时重传机制

这里重点介绍下这个超时重传,也就是解决UDP数据包可能丢失的问题。

首先要知道,DTLS只保证握手阶段数据包的可靠传输,应用数据是不保证的——当然DTLS保证即使应用数据包乱序和丢失,也能正常解密收到的数据。

DTLS将握手分为几个”flight”,每个”flight”其实是一端发送的一组消息,这组数据可以连续发出去,中间不会再收取对端的消息。每个”flight”当成一个整体,如果重传,就重传这一组消息。如下划分:

Client                                          Server
------ ------

ClientHello --------> Flight 1

<------- HelloVerifyRequest Flight 2

ClientHello --------> Flight 3

ServerHello \
Certificate* \
ServerKeyExchange* Flight 4
CertificateRequest* /
<-------- ServerHelloDone /

Certificate* \
ClientKeyExchange \
CertificateVerify* Flight 5
[ChangeCipherSpec] /
Finished --------> /

[ChangeCipherSpec] \ Flight 6
<-------- Finished /

Figure 1. Message Flights for Full Handshake

下边是会话恢复时候的flight划分

Client                                           Server
------ ------

ClientHello --------> Flight 1

ServerHello \
[ChangeCipherSpec] Flight 2
<-------- Finished /

[ChangeCipherSpec] \Flight 3
Finished --------> /

Figure 2. Message Flights for Session-Resuming Handshake
(No Cookie Exchange)

发送和重传的详细过程例子:client发送”flight 3”消息后,就建立个定时器,如果一段时间没有收到全部的 “flight 4” 消息,client就认为需要重传”flight 3”。server发送完”flight 4”后,也会建立个定时器,如果收到了client重传过来的”flight 3”,就会重传”flight 4”。client在收到全部的”flight 4”后会清理掉”flight 3”的定时器,server在收到全部的”flight 5”消息后,会清理掉”flight 4”的定时器。
如果是最后一个flight,比如完整握手中server发送的”flight 6”,发送完成后,同样需要建立一个定时器,最少是默认MSL的两倍,用来等待 client “flight 5”的重传包或者是应用数据。如果超过了2*MSL或者等来了client发来的应用数据,server就可以清理掉该定时器。会话恢复中则是client的 “flight 3” 建立定时器。

以上的过程可以统一到下边这个状态机中:

              +-----------+
| PREPARING |
+---> | | <--------------------+
| | | |
| +-----------+ |
| | |
| | Buffer next flight |
| | |
| \|/ |
| +-----------+ |
| | | |
| | SENDING |<------------------+ |
| | | | | Send
| +-----------+ | | HelloRequest
Receive | | | |
next | | Send flight | | or
flight | +--------+ | |
| | | Set retransmit timer | | Receive
| | \|/ | | HelloRequest
| | +-----------+ | | Send
| | | | | | ClientHello
+--)--| WAITING |-------------------+ |
| | | | Timer expires | |
| | +-----------+ | |
| | | | |
| | | | |
| | +------------------------+ |
| | Read retransmit |
Receive | | |
last | | |
flight | | |
| | |
\|/\|/ |
|
+-----------+ |
| | |
| FINISHED | -------------------------------+
| |
+-----------+
| /|\
| |
| |
+---+

Read retransmit
Retransmit last flight

Figure 3. DTLS Timeout and Retransmission State Machine

因为是client先发送数据,所以client一开始进入的是”PREPARING”状态,server一开始进入的是”WAITING”状态。

PS: 重传消息的seq number不变。

4. 其他一些东西

  1. 最开始的 ClientHello 和 HelloVerifyRequest 不包含在最后 CertificateVerify 和 Finished 中HMAC的计算里
  2. 虽然一个握手消息会分成几个记录层数据包,但计算 CertificateVerify 和 Finished 中的hash的时候,仍会将分开的握手消息分片组成一个完整的握手消息去计算,毕竟计算握手副本hash的时候,用的只是握手数据,不包括记录层。
  3. 超时重传定时器的值,建议从1s开始,依次*2,最大值不少于 60s。
  4. 告警信息不会重传。
  5. epoch是每次加密状态变换的时候递增。

参考:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK