前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TLS协议分析 (六) handshake协议扩展

TLS协议分析 (六) handshake协议扩展

作者头像
用户8964349
修改2021-09-07 18:14:09
1.2K0
修改2021-09-07 18:14:09
举报
文章被收录于专栏:OpenIM

5.11. handshake — Finished

在 ChangeCipherSpec 消息之后,应该立即发送 Finished 消息,来确认密钥交换和认证过程已经成功了。ChangeCipherSpec 必须在其它握手消息和 Finished 消息之间。

Finished 消息是第一条用刚刚协商出来的参数保护的消息。接收方必须确认Finished消息的内容是正确的。一旦某一方发送了,并且确认了对端发来的Finished消息,就可以开始在连接上发送和接收应用数据了。

消息结构:

代码语言:javascript
复制
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消息之后发送。

代码语言:javascript
复制
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。如下图:

代码语言:javascript
复制
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失效。

代码语言:javascript
复制
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的格式像这样:

代码语言:javascript
复制
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格式序列化保存了下面这些字段:

代码语言:javascript
复制
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协议抓包:

TLS协议分析 (六) handshake协议扩展_im
TLS协议分析 (六) handshake协议扩展_im

7. Alert 协议

一种返回码机制,简单

代码语言:javascript
复制
  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协议:

TLS协议分析 (六) handshake协议扩展_im_02
TLS协议分析 (六) handshake协议扩展_im_02

8. application data协议

application data协议,就是把应用数据直接输入record层,做分段,算MAC,加密,传输。 抓包举例如下:

TLS协议分析 (六) handshake协议扩展_im_03
TLS协议分析 (六) handshake协议扩展_im_03

本文转自微信后台团队,如有侵犯,请联系我们立即删除

OpenIMgithub开源地址:

https://github.com/OpenIMSDK/Open-IM-Server

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

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

更多技术文章:

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

【OpenIM原创】简单轻松入门 一文讲解WebRTC实现1对1音视频通信原理 https://forum.rentsoft.cn/thread/4

【OpenIM原创】开源OpenIM:轻量、高效、实时、可靠、低成本的消息模型 https://forum.rentsoft.cn/thread/1

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 5.11. handshake — Finished
  • 5.12. handshake — NewSessionTicket
  • 6. ChangeCipherSpec 协议
  • 7. Alert 协议
  • 8. application data协议
相关产品与服务
SSL 证书
腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档