TLS 基于 Session ID 的会话恢复
在 TLS1.3 之前的 TLS 版本中, 可以通过 Session ID 字段来完成 Session Resumption.
Session ID: This is the identity of the session corresponding to this connection
那么具体实现如何呢,我们一起来看看。
流程
初始连接 (完整的握手后建立)
首先,在 ClientHello 和 ServerHello 都附带 Session ID 字段。
当客户端一次尝试与服务器通信时,它通常没有任何可以恢复的 tls session, 因此,需要进行一次完整的 tls 握手流程。 这个流程中以下点需要注意:
- 
通常 ClientHello 中的 Session ID 设置为空。
 - 
ServerHello 中的 Session ID 不为空。
 
在这次握手成功之后,我们便有了一个可以在其他 tls 链接中使用的可恢复的 session 了。
用在当前连接中,即将被恢复的 session 可以是:
- 之前的 session(现在已经断开)
 - 现有 session(连接成功,正在使用中),
 - 或者从其他握手成功的 tls 中获取的 session。
 
完整的握手流程大体如下 (实际中可能略有不同):
Client ServerClientHello (SessionID=empty) -----><----- ServerHello (SessionID=not empty)CertificateServerKeyExchangeServerHelloDoneClientKeyExchange ----->ChangeCipherSpecFinished<------ ChangeCipherSpecFinished
如果想要从当前 session 建立其他 session, 需要保存以下信息:
- Session ID (ServerHello中)
 - Master Secret (由当前 TLS 握手生成)
 - CipherSuite (由当前 TLS 握手生成)
 - Host name (只有将来连接到相同的 TLS server 时,从能使用当前信息进行 session恢复)
 - 其他
 
从已有 session 建立一个新的 TLS 连接
从已有 session 建立新的连接的规则如下:
当 ClientHello 中的 Session ID 字段不为空时, 服务器便需要在自己的 session 的缓存中查找是否存在对应的 session:
- 当找到对应的 session, 并且服务器决定使用这个 session, 它便会把这个 
Session ID回复给客户端,来表明服务器端接受客户端的 session 恢复请求。 - 如果服务器没有找到缓存的 session, 或者不想恢复找到的 session, 它会给客户端回复一个全新的  
Session ID。 如果回复的Session ID是空,表明当前 session‘ 不能被恢复。 
恢复之后的 session 的 Cipher suite 应该和被恢复的 session 相同。
对于这个连接,有以下点需要注意:
ClientHello中需要包含一个不为空的Session ID, 表明想要恢复的 TLS session 状态。- 如果服务器回复的 
ServerHello中 包含了相同的Session ID, 那么 session恢复成功。 否则,session 恢复失败。 
通常情况下,session 恢复失败不会导致 TLS 连接的失败。 而是需要想第一个连接那么,进行完整的握手流程。
此时的握手流程大体如下 (实际中可能略有不同):
Client ServerClientHello (SessionID=A) -----><----- ServerHello (SessionID=A)ChangeCipherSpecFinishedChangeCipherSpec ----->Finished
这里,session 恢复成功之后,使用对应的 MasterSecret, CipherSuite 对当前 TLS 通信过程中的数据进行加密解密
实例解析
1. 第一个 ClientHello
Handshake Protocol: Client HelloHandshake Type: Client Hello (1)Version: TLS 1.2 (0x0303)Random: 6......373a7726385Session ID Length: 0......
Session ID: 空。 期待进行完整的 TLS 握手流程
2. 第一个 ServerHello
Handshake Protocol: Server HelloHandshake Type: Server Hello (2)Version: TLS 1.2 (0x0303)Random: 1fc19c...01Session ID Length: 32Session ID: 756e6c6055fac1d3f4e23e19756d066dfb09bf847f6f16045feda2270cc7f307Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)....
Session ID: 服务器端生成了一个 session id, 用来标识当前 TLS session。
CipherSuite:这个属性也需要被记录,以便后续使用
3. Session Resumption 的 ClientHello
Handshake Protocol: Client HelloHandshake Type: Client Hello (1)Random: 64e32....5a5fSession ID Length: 32Session ID: 756e6c6055fac1d3f4e23e19756d066dfb09bf847f6f16045feda2270cc7f307Cipher Suites Length: 116Cipher Suites (58 suites)....
Session ID: 这里,我们使用步骤2中相同的 Session ID来标识,我们想要从之前的那个 TLS session 中恢复, 以此建立一个新的 TLS session。
4. Session Resumption 的 ServerHello
Handshake Protocol: Server HelloHandshake Type: Server Hello (2)Version: TLS 1.2 (0x0303)Random: 276c4e4....4e47524401Session ID Length: 32Session ID: 756e6c6055fac1d3f4e23e19756d066dfb09bf847f6f16045feda2270cc7f307Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)Compression Method: null (0)
Session ID: 可以看到,这里的 Session ID 与客户端发送的 ClientHello 中的 session id 相同, 标识此次 sesssion 恢复成功。