乐趣区

关于im:TLS协议分析-六-handshake协议扩展

5.11. handshake — Finished

在 ChangeCipherSpec 音讯之后,应该立刻发送 Finished 音讯,来确认密钥替换和认证过程曾经胜利了。ChangeCipherSpec 必须在其它握手音讯和 Finished 音讯之间。

Finished 音讯是第一条用刚刚协商进去的参数爱护的音讯。接管方必须确认 Finished 音讯的内容是正确的。一旦某一方发送了,并且确认了对端发来的 Finished 音讯,就能够开始在连贯上发送和接管利用数据了。

音讯构造:

struct {opaque verify_data[verify_data_length];
} Finished;

verify_data
   PRF(master_secret, finished_label,Hash(handshake_messages))
      [0..verify_data_length-1];

finished_label
   对客户端发的 Finished 音讯来说,固定是字符串 "client finished".  
   对服务器发的 Finished 音讯来说,固定是字符串 "server finished".1.2.3.4.5.6.7.8.9.10.11.

Hash 示意握手音讯的 hash。hash 函数是前文 PRF 的 hash 函数。或者 CipherSuite 规定的用于 Finished 计算的 hash 函数。

在 TLS 的之前版本中,verify_data 总是 12 字节。在 TLS 1.2 中,这取决于 CipherSuite。如果 CipherSuite 没有显式规定 verify_data_length,就当成 12 字节解决。未来的 CipherSuite 可能会规定别的长度,然而不能小于 12 字节。

Finished 音讯必须跟在 ChangeCipherSpec 音讯之后,如果程序错乱,就是 fatal error.

handshake_message 的内容蕴含从 ClientHello 开始,直到 本条 Finished 之前的所有音讯,只蕴含 handshake 层的音讯体,不蕴含 record 层的几个音讯头字段。包含 CertificateVerify 音讯。同时,对客户端和服务器来说,handshake_message 的内容不同,后发送者必须蕴含前发送者的 Finished 音讯。

留神:ChangeCipherSpec 音讯,alert,和其它的 record 类型不是握手音讯,不蕴含在 hash 计算中。同时,HelloRequest 音讯也不算在内。

5.12. handshake — NewSessionTicket

SessionTicket 定义在 RFC5077 规范外面,2008 年公布。

SessionTicket 是一种不须要服务器端状态的,复原 TLS session 的形式。
SessionTicket 能够用于任何 CipherSuite。TLS 1.0, TLS 1.1, TLS 1.2 都实用。

在上面这些场景下,尤其有用:

用户量微小,session id 的形式消耗服务器内存过多
服务器心愿长时间缓存 session
服务器有多台,不心愿服务器间有共享状态
服务器内存不足
客户端在 ClientHello 中设置一个 SessionTicket 扩大来标识本人反对 SessionTicket。如果客户端本地没有存之前收到的 ticket,就把这个扩大设为空。

如果服务器心愿应用 SessionTicket 机制,服务器把本地的 session 状态存入一个 ticket 中,ticket 会被加密,并被 MAC 爱护,无奈篡改,加密和算 MAC 用的 key 只有服务器晓得。
加密并 MAC 过的 ticket 用 NewSessionTicket 音讯分发给客户端,NewSessionTicket 音讯应该在 ChangeCipherSpec 音讯之前,在服务器验证通过客户端的 Finished 音讯之后发送。

Client                                               Server
ClientHello
(empty SessionTicket extension)------->
                                                ServerHello
                            (empty SessionTicket extension)
                                               Certificate*
                                         ServerKeyExchange*
                                        CertificateRequest*
                             <--------      ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished                     -------->
                                           NewSessionTicket
                                         [ChangeCipherSpec]
                             <--------             Finished
Application Data             <------->     Application Data

Figure 1: Message flow for full handshake issuing new session ticket1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

客户端把收到的 ticket 和 master secret 等其它与以后 session 无关的参数一起,缓存起来。
单客户端心愿复原会话时,就把 ticket 蕴含在 ClientHello 的 SessionTicket 扩大中发给服务器。
服务器收到后,解密 ticket,算 MAC 确认 ticket 没有被篡改过,而后从解密的内容外面,获取 session 状态,用来复原会话。如果服务器胜利地验证了 ticket,能够在 ServerHello 之后返回一个 NewSessionTicket 音讯来更新 ticket。

显然,这种状况下,相比残缺握手,能够省掉 1 个 RTT。如下图:

Client                                                Server
ClientHello
(SessionTicket extension)      -------->
                                                 ServerHello
                             (empty SessionTicket extension)
                                            NewSessionTicket
                                          [ChangeCipherSpec]
                              <--------             Finished
[ChangeCipherSpec]
Finished                      -------->
Application Data              <------->     Application Data
  Figure 2: Message flow for abbreviated handshake using new
                        session ticket1.2.3.4.5.6.7.8.9.10.11.12.13.

如果服务器不能,或者不想应用客户端发来的 ticket,那服务器能够疏忽 ticket,启动一个残缺的握手流程。

如果服务器此时不心愿下发新的 ticket,那就能够不回复 SessionTicket 扩大,或者不回复 NewSessionTicket 音讯。
此时除了 ClientHello 外面的 SessionTicket 扩大,就和个别的 TLS 流程一样了。

如果服务器回绝收到的 ticket,服务器可能依然心愿在残缺的握手之后,下发新的 ticket。
此时流程和全新 ticket 生成下发的区别,就是 ClientHello 的 SessionTicket 不是空的。

NewSessionTicket 音讯
服务器在握手过程中,发 ChangeCipherSpec 之前发送 NewSessionTicket 音讯。
如果服务器在 ServerHello 中蕴含了一个 SessionTicket 扩大,那就必须发送 NewSessionTicket 音讯。
如果服务器没有蕴含 SessionTicket 扩大,那相对不能发送 NewSessionTicket 音讯。
如果服务器在蕴含了 SessionTicket 扩大之后,不想发送 ticket,那能够发送一个长度为 0 的 NewSessionTicket 音讯。

在残缺握手的状况下,客户端必须在确认服务器的 Finished 音讯正确之后,能力认为 NewSessionTicket 外面的 ticket 非法。

服务器能够 NewSessionTicket 音讯中更新 ticket。

ticket_lifetime_hint 字段蕴含一个服务器的提醒,提醒客户端本 ticket 应该存多长时间就生效。单位是秒,网络字节序。当工夫到期时,客户端应该删掉 ticket 和关联的状态。客户端也能够提前删除。服务器端也能够提前认为 ticket 生效。

struct {
    uint32 ticket_lifetime_hint;
    opaque ticket<0..2^16-1>;
} NewSessionTicket;1.2.3.4.

SessionTicket 和 Session ID 之间的关系比拟繁琐。感兴趣的自行去看 RFC 吧。

对于客户端来说,ticket 就是一块二进制 buffer,客户端并不论外面的内容。所以 ticket 具体怎么加密加 MAC 服务器能够随心所欲,无需顾及客户端的感触。

RFC5077 中举荐了一种 ticket 的加密爱护办法:
服务器应用 2 个 key,一个 aes-128-cbc 的 key,一个 HMAC-SHA-256 的 key。

ticket 的格局像这样:

struct {opaque key_name[16];
    opaque iv[16];
    opaque encrypted_state<0..2^16-1>;
    opaque mac[32];
} ticket;1.2.3.4.5.6.

其中,key_name 用来标识一组 key,这样服务器端就能够应用多组 key。

加密过程,首先随机生成 IV,而后用 aes-128-cbc 加密 session 的序列化后果,
而后用 HMAC-SHA-256 对 key_name,IV,encrypted_data 的长度(2 字节),encrypted_data 计算 MAC。
最好把各个字段填入下面 ticket 构造体。
显然,此处是 Encrypt-then-MAC 的形式,是最平安的。

理论在 openssl 中的 session,用 asn1 格局序列化保留了上面这些字段:

typedef struct ssl_session_asn1_st {
    ASN1_INTEGER version;
    ASN1_INTEGER ssl_version;
    ASN1_OCTET_STRING cipher;
    ASN1_OCTET_STRING master_key;
    ASN1_OCTET_STRING session_id;
    ASN1_OCTET_STRING session_id_context;
    ASN1_INTEGER time;
    ASN1_INTEGER timeout;
    ASN1_INTEGER verify_result;
    ASN1_OCTET_STRING tlsext_hostname;
    ASN1_INTEGER tlsext_tick_lifetime;
    ASN1_OCTET_STRING tlsext_tick;
} SSL_SESSION_ASN1;1.2.3.4.5.6.7.8.9.10.11.12.13.14.

6. ChangeCipherSpec 协定

ChangeCipherSpec 用来告诉对端,开始启用协商好的 Connection State 做对称加密,内容只有 1 个字节。
这个协定是冗余的,在 TLS 1.3 外面间接被删除了。

changeCipherSpec 协定抓包:

7. Alert 协定

一种返回码机制,简略

  enum {warning(1), fatal(2), (255) } AlertLevel;

  struct {
      AlertLevel level;
      AlertDescription description;
  } Alert;1.2.3.4.5.6.

其中 level 是等级,不同等级要求不同的解决。

其中有一种:close_notify,用来告诉对端,我不会再发送更多数据了。这个能够让对端被动 close fd,这样能够缩小我方 tcp timewait 状态的 socket 量。

alert 协定:

8. application data 协定

application data 协定,就是把利用数据间接输出 record 层,做分段,算 MAC,加密,传输。
抓包举例如下:

本文转自微信后盾团队, 如有进犯, 请分割咱们立刻删除

OpenIMgithub 开源地址:

https://github.com/OpenIMSDK/…

OpenIM 官网: https://www.rentsoft.cn

OpenIM 官方论坛: https://forum.rentsoft.cn/

更多技术文章:

开源 OpenIM:高性能、可伸缩、易扩大的即时通讯架构
https://forum.rentsoft.cn/thr…

【OpenIM 原创】简略轻松入门 一文解说 WebRTC 实现 1 对 1 音视频通信原理
https://forum.rentsoft.cn/thr…

【OpenIM 原创】开源 OpenIM:轻量、高效、实时、牢靠、低成本的音讯模型
https://forum.rentsoft.cn/thr…

退出移动版