浅析 HTTPS (SSL/TLS) 握手协议
目录
超文本传输安全协议 (HTTPS, Hypertext Transfer Protocol Secure)
常称为HTTP over TLS,HTTP over SSL或HTTP Secure, 是一种使用计算器网络进行安全通信的传输协议。HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。这个协议由网景公司(Netscape)在1994年首次提出,随后扩展到互联网上。
传输层安全性协议 (TLS, Transport Layer Security)
,及其前身 安全套接层(SSL,Secure Sockets Layer)
是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障。网景公司推出HTTPS协议时,以SSL进行加密,这是SSL的起源。IETF将SSL进行标准化,1999年公布第一版TLS标准文件。随后又公布RFC 5246 (2008年8月)与 RFC 6176 (2011年3月)。TLS/SSL 不仅为浏览器提供支持,在邮箱、即时通信、VoIP、网络传真等应用程序中也得到广泛应用。
由此可知,HTTPS 是在 SSL/TLS 之上承载 HTTP 。与HTTP 不同,HTTPS 使用 https://
做为URL前缀, 默认端口为 443
.
为方便,后面将 SSL/TLS 简写为 TLS.
协议的层次
TLS 协议为由 TLS记录协议(TLS record protocol)
与 TLS握手协议(TLS handshake protocol)
这两层协议叠加而成。记录协议负责进行加密,握手协议负责进行加密之外的其它操作。
- TLS记录协议 位于握手协议的下层,负责对消息进行加密。它使用了对称加密和消息认证,但具体的算法和共享密钥则是通过握手协议在服务端和客户端之间协商决定的。
- TLS握手协议 分为4个子协议
- 握手协议 负责在客户端与服务端之间协商决定密码算法和共享密钥。基于证书的认证操作也是在这个协议中完成。在握手协商一致后,双方会互发信号来切换密码。
- 密码规格变更协议 负责向通信对象传达密码变更的信号。
- 警告协议 负责在发生错误时将错误信息传递给对方。如果没有发生错误,接下来会使用应用数据协议进行通信。
- 应用数据协议 负责将TLS 上面承载的应用数据传达给通信对象
TLS记录层协议
记录层协议负责对消息进行压缩、加密以及数据的认证。
原始消息将被分成多个较短的片段(segament),每个片段将被压缩、添加认证码、然后加密并添加片段头(类型、版本、长度)。
认证使用的单身散列函数与压缩使用使用算法以及共享密钥都需要两端协商决定。加密使用的CBC模式, 初始向量(IV) 通过主密码(master secret)生成。多个片段最终组合成报文。
握手协议
握手协议是TLS握手协议的一部分,负责生成共享密钥以及交换证书。这一部分的信息交换是在没有加密的情况下进行的,必须使用公钥密码事 Diffie-Hellman 密钥交换。
握手协议的过程如下:
1. 客户端发送 "ClientHello" 消息
type : 1
内容包括:
- 可用的版本号
- 当前时间
- 客户端随机数
- 会话 ID
- 可用的密码套件清单
- 可用的压缩算法清单
密码套件(cipher suite)
是一种密码算法的搭配集合。每个套件定义一个密码交换算法、一个批量加密算法, 一个消息认证(MAC)算法以及一个伪随机函数(PRF). 例如 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 它包含:
- 密码交换算法,ECDHE_RSA , 用于决定握手时如何验证身份
- 批量加密算法, AES_128_GCM ,用于加密消息流。
- 消息认证码算法, SHA256, 用于创建消息摘要,以及决定伪随机函数的算法。
- 伪随机函数,例如TLS 1.2的伪随机函数使用MAC算法的散列函数来创建一个主密钥——连接双方共享的一个48字节的私钥。主密钥在创建会话密钥(例如创建MAC)时作为一个熵来源
密码套件技术意味着加码可以自由切换,当一种算法被证明不能胜任时,可以方便地把它换掉。当然,每一个套件都是事先搭配好的,每个套件有一个ID, 这个ID 是通信双方都认可的。
2. 服务端发送 "ServerHello" 消息
type: 2
内容包括:
- 使用的版本号
- 当前时间
- 服务器随机数
- 会话ID
- 使用的密码套件
- 使用的压缩方式
3. Certificate 服务端发送证书
type: 11
内容包括:
- 证书清单
证书清单
是一组 X.509v3
证书序列,首先是服务器的证书,然后依次是对前一个证书签名的认证机构的证书。
4. ServerKeyExcange 服务端要求进行密钥交换
type: 12
当证书不足以满足需求时,服务端会请求进行密钥交换,其内容需要密码套件的不同而不同
5. CertificateRequest 服务端请求客户端证书
type: 13
只有当需要客户端认证时才发送该消息。包括:
- 服务端能理解的证书类型清单
- 服务端能理解的认证机构名称清单
6.ServerHelloDone 服务端完成握手
type: 14
这一信息表示从 2.
到 5.
的过程正式结束
7. 客户端 Certificate
type: 11
当 5.
服务端要求客户端发送证书时,客户端会将自己的证书发送给服务端,由服务端进行验证。否则不发送此消息
8. ClientKeyExchange 客户端密钥交换
type: 16
当套件中包含 RSA 时,会随 ClientKeyExchange 发送一个经过服务器公钥加密的 预备主密码(pre-master secret)
。这是一个随机数,之后会被用作生成主密码的种子。
当套件中包含 DH 密钥交换时,会随 ClientKeyExchange 发送一个 DH 公开值。
9. CertificateVerify 客户端认证
type: 15
只有在 5.
服务端请求 Certificate 后才会发送此消息,其目的是向服务端证明自己确实持有客户端证书和私钥。
10 Finished 客户端结束握手
type: 20
客户端的 "Finished" 是Client握手协议的最终消息。该消息是一段加密信息,其服务端必须验证其内容是否正确,如果不正确,将返回 "decrypt_error" 警告。
11. NewSessionTiclet
type: 4
当 SSL 连接意外中断以后,需要重新握手。此时有两种方法可以恢复原来的会话:一种是 SessionID, 另一种是 SessionTicket。
使用 SessionID, 重连时客户端只需要给出 该ID ,且服务端有该 ID,双方就可以重新使用该ID进行通信。它的缺点在于,SessionID 往往只保留在一台服务器上,如果客户端发送请求到另一台服务器,就无法恢复对话。SessionTicket 就是为了解决该问题而诞生的。SessionTicket 是服务端生成的唯一标识,加密后发给客户端,只有服务端才能解密。当服务端收到 SessionTicket 后,就不需要重新生成SessionID了。
当服务端收到客户端的 Finished 消息之后 ,可能在任一时间发送 NewSessionTicket 消息
12. Finished 服务端结束握手
type: 20
小节
从上述来看,握手协议完成了一下操作:
- 客户端获得服务器公钥,完成服务器认证
- 服务器获得客户端公钥,完成客户端认证(当需要进行客户端认证是时)
- 双方生成 了加密通信中使用的共享密钥
- 双方生成了消息认证码中使用的共享密钥
整个过程可以参考下图:
根据 rfc5246(TLS1.2) 与 rfc8446(TLS1.3) 规定,握手协议中各操作的 HandshakeType
分别为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
hello_request(0), client_hello(1), server_hello(2), new_session_ticket(4), end_of_early_data(5), encrypted_extensions(8), certificate(11), server_key_exchange (12), certificate_request(13), server_hello_done(14), certificate_verify(15), client_key_exchange(16), finished(20), key_update(24), message_hash(254), |
实践:通过 Wireshark 了解握手过程
此次实践使用 Wireshark 过捕获 通过浏览器访问本站(https://code.wandoer.com) 的数据包,来分析握手协议
设置过滤器为 ip.dst == 121.42.232.178 || ip.src == 121.42.232.178 && ssl
,并访问本站,得到如下结果:
(1) 第一个包是 ClientHello
来看看具体内容:
HEAD 为 16 03 01 02 00
。0x16
为ContentType:HandShake, 0x03 0x01
表示版本号 1.0, 0x02 0x00
表示段数据的长度。这是一个 记录层协议,在其中又包含了握手协议的数据: 01 00 01 fc
, 0x01
表示 'ClientHello', 后面是数据段的长度。
接着是段内的数据:
- 版本号 1.2
- 时间 : GMT Unix Time: Oct 10, 1978 01:05:32.000000000 中国标准时间
- 随机数: a83aa189777fc65006c76f562f1cf2778664f49b53bb8404...
- SessionID : ec8ce56206fa0c2ab2f272f380ddabb51f9a8b4109462cfc...
- 密码套件:共14个密码套件
- 压缩方法: 共一个压缩方法 (null不压缩)
(2) 然后是 ServerHello
段内数据:
- 版本号 1.2
- 时间: GMT Unix Time: Oct 29, 1970 09:37:56.000000000 中国标准时间
- 随机数: c0f0c2ec4ee69a54c8459080a305b02f5d34ff3b785b4324...
- SessionID: 长度为0 ,省略
- 选用的密码套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
- 选择的压缩算法: null 不压缩
(3) 接着服务器向客户端发送了 Certificate
这里发送了证书清单。
证书清单里首先是本站的证书(wandoer.com),然后是给本站颁发证书的机构(Let's Encrypt)的证书,然后是给 "Let's Encrypt"颁发证书的机构的证书…… 以此递归
(4) 接着是服务端进行密钥交换,并结束了 ServerHello
这里使用前面选定的套件的ECDHE, 椭圆曲线密钥交换,给出了椭圆名称,公钥等信息。
然后服务端发送了 0x0e
ServerHelloDone, 结束了ServerHello, 接下来该客户端回话了。
(5) Client发送的消息如下:
包括:
- 密钥交换(
0x10
), 发送了客户端公钥 - 长度为 40 个字节的加密的 Finished 消息
它还包含一个 "Change Cipher Spec" 消息,这已经属于密码规格变更协议的部分了。
(6) 然后服务端发送的消息:
包括
- New Session Ticket(
0x04
),发送了SessionTicket - 长度为 40 个字节的加密的 Finished 消息
以及一个属于密码规格变更协议的 "Change Cipher Spec" 消息
在此之后,双方将各自完成密码规格变更,然后双方通信的内容就都是加密后的应用数据了。
干货满满~