HTTPS协议详解:TLSSSL握⼿过程1、握⼿与密钥协商过程
基于RSA握⼿和密钥交换的客户端验证服务器为⽰例详解TLS/SSL握⼿过程
再看⼀张⼿绘时序图
(1).client_hello
在发送的 Client Hello 中会带上⾃⼰⽀持的加密算法,供服务端从中挑选。由于⽼旧客户端会⽀持⼀些不安全的加密算法,为了提⾼传输安全,通常会在服务端指定⼀个可⽤算法列表,最终使⽤的加密类型取决于⼆者的交集,并按服务端优先级取第⼀个;如果没有交集,直接终⽌会话。在 Nginx 中这个功能通过将 ssl_prefer_server_ciphers 设置为 on 开启。
客户端发起请求,以明⽂传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数,扩展字段等信息,相关信息如下:
· ⽀持的最⾼Tls协议版本version,从低到⾼依次 SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2,当前基本不再使⽤低于 TLSv1 的版本;· 客户端⽀持的加密套件 cipher suites 列表, 每个加密套件对应前⾯ TLS 原理中的四个功能的组合:认证算法 Au (⾝份验证)、密钥交换算法 KeyExchange(密钥协商)、对称加密算法 Enc (信息加密)和信息摘要 Mac(完整性校验);
· ⽀持的压缩算法 compression methods 列表,⽤于后续的信息压缩传输;
· 随机数 random_C,⽤于后续的密钥的⽣成;
· 扩展字段 extensions,⽀持协议与算法的相关参数以及其它辅助信息等,常见的 SNI 就属于扩展字段,
后续单独讨论该字段作⽤。
SSL报⽂格式可以⼤致分为2部分,Record层 和 Data层,Record层中指定了后续数据的类型,SSL版本(⼀般来说固定),以及后续数据的长度。
例如上图中显⽰的那样,SSL报⽂头部是TVL格式:
Content Type: Handshake
Version: TLS 1.0
Length: 121
这些表⽰后续的Data层是握⼿报⽂,TLS1.0格式,总长度是121字节。
⽽Data层,⼜有⾃⼰的格式,根据不同的类型,⽐如Client Hello,Certificate,等,有⾃⼰的格式(所以Record中的Content Type字段是必要的),并且不同的版本之间也有不同的格式,例如TLS1.0和SSL 3.0对Client key exchange报⽂有⾃⼰的组织⽅式(所以Record 中的Version字段也是必要的)。长度字段有效的确定了单个SSL报⽂结束的位置,因为SSL报⽂承载与TCP之上,⼀个TCP段中存在多个SSL报⽂是⾮常常见的事情,如果没有字段描述单个SSL报⽂的长度,那么解析报⽂将变得不可能。
(a)Random
随机数,⼀共32字节,其中前4个字节使⽤系统当前时间,后28字节使⽤伪随机函数⽣成的随机数。4个字节以Unix时间格式记录了客户端的协调世界时间(UTC)。协调世界时间是从1970年1⽉1⽇开始到当前时刻所经历的秒数,那么时间是不断的上涨的,通过前4字节填写时间⽅式,有效的避免了周期性的出现⼀样的随机数。使得“随机”更加“随机”。随机数⽤来⼲什么?随机数是⽤来⽣成对称密钥的,我们后续讲到⽣成对称密钥的时候,会再次提到随机数。现在只需要记住“客户端⽣成了⼀个随机数,然后发送到的服务器”。
我们后续讲到⽣成对称密钥的时候,会再次提到随机数。现在只需要记住“客户端⽣成了⼀个随机数,然后发送到的服务器”。
然⽽在具体的实现上,不同客户端⾏为不⼀样,IE会带时间,⽽Firefox就不带时间,因为它本⾝的含义就是随机数,⽽对端也不会校验这个时间(和tcp的时间戳完全不是⼀回事⼉)。
随机数参与了SSL握⼿的 master key的计算、KDF计算、server_key_exchange的签名值的计算。属于混淆的⼀部分。
(b)Session id
标识了后⾯Session id的长度,但是对于⼀般新建的会话,Session id length都是0,如果Session id length有值,对于 SSL 2.0 Session id length0~16字节,其后的版本扩⼤到32字节。本报⽂中Session id length是0,后⾯就没有跟Session id,直接是Cipher suit length。下⾯是Session id不是0的报⽂:
⼀眼就能看出差别了把!Session id在会话复⽤中被⽤到,我们讲到会话复⽤的时候,会提及Session id,本章分析报⽂时,不会再提及Session id。
Session id并不⼀定是32字节,RFC规定可以0~32字节。只是Session id由服务器⽣成,服务器普遍采⽤OpenSSL,⽽OpenSSL基本只⽣成32字节的session id,如果碰到其他字节长度的Session id,切莫认为是异常client hello。
(c)Cipher Suite
Cipher Suits Length:含义同Session id length
Cipher Suits:加密套件,它列出了客户端能够⽀持的加密⽅式、算法等信息。不同的加密套件性能不⼀样,安全性不⼀样,也导致了SSL 交互报⽂的不⼀样。
最常见的如TLS_RSA_WITH_RC4_128_SHA1,它表⽰我们密钥交换使⽤RSA,⾝份认证算法⽤RSA(咦,怎么和之前说的不⼀样,⾝份认证难道不是证书吗?是的,证书是⾝份认证⼀部分,我们还需要⽤RSA去验证证书,后⾯会讲到),对称加密算法使⽤RC4,摘要算法使⽤SHA1。再举个例⼦,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA1,它表⽰,密钥交换算法使⽤ECDHE(短暂椭圆曲线),⾝份认证算法使⽤ECDSA,对称加密算法使⽤AES_128_CBC,摘要算法使⽤SHA1。
客户端把⾃⼰所⽀持的加密套件全部发送给服务器,服务器会从中选择⼀个加密套件。这个我们将在后⾯的Server hello中讲到。
每个加密套件⽤2字节表⽰,这⾥⼀共12个加密套件,所以理所当然的,Cipher Suits Length是12 * 2 = 24字节。
(d)Compression Methods
⽬前没见过使⽤压缩算法的实例。这⾥不再拓展。
(e)Extension
拓展字段的存在,是因为SSL协议起草之初有些功能没有考虑到,后续这些功能被加进RFC,⽽为了兼容SSL,把这些功能的描述放到Extension中。Extension很⼤程度上影响了SSL的流程,很多⼈觉得某些SSL连接的报⽂好奇怪、和正常接触的SSL报⽂不⼀样,就是因为Extension起的作⽤。
TLS 1.3 就借助 extension 发送不同curve的pubkey来减少RTT,后续有机会再说。这⾥介绍主流的⼀些拓展。
(a)Renegotiation info:
如果是重新协商(即加密的hello),那么会带上上次协商的12字节的finished,如果是新hello请求,那么⾥⾯字段为0。
切记及时服务器端不⽀持renegotiation,在server hello响应时也需要带上这个拓展(前提是客户端有这个拓展或者有等价的
EMPTY_SCSV,因为我在实现上碰到过如果不带该拓展,导致客户端结束连接的情况)。
(b)Server_name(SNI):
SSL存在验证证书的时候,有这么⼀个判断:⽐较“浏览器输⼊的地址”和“获取的证书的名称”,如果⼀样,那么接着验证,如果不⼀样,那么认为证书是不可信的。
假设有公司的域名存在2个:和,它们对应的ip都是222.12.34.56,假设服务证书的名字是 ”www.123”  ,那么从
”www.567” 访问过来的⽤户⽆法信任服务器证书,总之,总有⼀个域名访问会导致客户端⽆法信任服务器。为了解决这个问题,客户端在client hello中带上server name 拓展(如果使⽤ip地址进⾏访问,那么就不会有server name拓展),它会捎带上域名地址,服务器解析到server name后,就会根据server name中的域名,选择合适的证书。
(c)Elliptic_curves/ec_point_formats:
使⽤椭圆曲线密钥交换算法的时候⽤到,⾥⾯列举了⾃⼰⽀持的椭圆曲线算法,供服务器选择。
(d)application_layer_negotiation(ALPN):
⽤以描述⽀持的上层协议,h2、http/1.1、spdy等。可以把他想象成IP头中的protocol,描述了上层是TCP还是UDP还是ICMP。
⽬前主流浏览器,若要使⽤HTTP2,则必须使⽤这个拓展。
(e)status_request
请求OCSP,服务器可以发送cettificate status到客户端,⾥⾯带上ocsp的信息。
(f)signature_algorithms
表⽰⾃⼰⽀持的签名算法,服务器收到这个拓展,在进⾏例如server key exchange签名等操作时,需要参考客户端这个拓展。
(g)SessionTicket TLS
Session ticket会话复⽤时使⽤,讲到session ticket时再说。
注意,⽼的⼀些客户端没有extensions length字段,⽽新的客户端如果不携带extension,他也会有extensions length,只是为0。所以判断有没有extension 存在的⽅式是compression methods是否在client hello的最后
(2).server_hello+server_certificate+sever_hello_done
·
server_hello, 服务端返回协商的信息结果,包括选择使⽤的协议版本 version,选择的加密套件 cipher suite,选择的压缩算法compression method、随机数 random_S 等,其中随机数⽤于后续的密钥协商;
· server_certificates, 服务器端配置对应的证书链,⽤于⾝份验证与密钥交换;
· server_key_exchange, 如果是DH算法,这⾥发送服务器使⽤的DH参数。RSA算法不需要这⼀步
· certificate_request, Certificate Request 是服务端要求客户端上报证书,这⼀步是可选的,对于安全性要求⾼的场景会⽤到· server_hello_done,通知客户端 server_hello 信息发送结束;
主要包含⼏点重要的信息
1:确定协商使⽤的TLS版本号(TLS1.0 or TLS 1.1 or TLS 1.2)。
2:随机数(⽤来⽣成主密钥和kdf)
3:session id(sessionid会话复⽤需要带上,当然命中session
ticket时也需要带上)
4:使⽤的加密套件(根据客户端提供的算法列表、服务器证书类型、服务器配置决定)
5:拓展。⼀般会带上空的renegotiation info(前提是客户端提供了EMPTY_SCSV加密套件或者有renegotiation
info拓展),以表⽰⾃⼰⽀持secure renegotiation,我碰到过不带次拓展,客户端直接断开的情况。原因未知。
(3).证书校验
客户端验证证书的合法性,如果验证通过才会进⾏后续通信,否则根据错误情况不同做出提⽰和操作,合法性验证包括如下:
· 的可信性 trusted certificate path,⽅法如前⽂所述;
· 证书是否吊销 revocation,有两类⽅式离线 CRL 与在线 OCSP,不同的客户端⾏为会不同;ssl协议全称
· 有效期 expiry date,证书是否在有效时间范围;
· 域名 domain,核查证书域名是否与当前的访问域名匹配,匹配规则后续分析;
(4).client_key_exchange+change_cipher_spec+encrypted_handshake_message
(a) client_key_exchange,合法性验证通过之后,客户端计算产⽣随机数字 Pre-master,并⽤证书公钥加密,发送给服务器;