TLS 握手的前几个消息都是明文的,能够在 Wireshark 里直接看。
但只要出现了“Change Cipher Spec”,后面的数据就都是密文了,看到的也就会是乱码,不知道究竟是什么东西。
为了更好地分析 TLS 握手过程,可以再对系统和 Wireshark 做一下设置,让浏览器导出握手过程中的秘密信息,这样 Wireshark 就可以把密文解密,还原出明文。
首先,你需要在 Windows 的设置里新增一个系统变量SSLKEYLOGFILE
,设置浏览器日志文件的路径,比如
D:http_studylogsslkey.log
然后在 Wireshark 里设置“Protocols-TLS”
(较早版本的 Wireshark 里是“SSL”),在“(Pre)-Master-Secret log filename”里填上刚才的日志文件
ECDHE握手过程
在 TCP 建立连接之后,浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。
里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 512
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 508
Version: TLS 1.2 (0x0303)
Random: deb646e741a099b49bfef08f38871a4ac8d86b1f265ea6dc…
Session ID Length: 32
Session ID: 559393da63fd63f325d1f52088aa649401260c65f1e499fc…
Cipher Suites Length: 34
Cipher Suites (17 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 401
Extension: Reserved (GREASE) (len=0)
Extension: server_name (len=19)
Extension: extended_master_secret (len=0)
Extension: renegotiation_info (len=1)
Extension: supported_groups (len=10)
Extension: ec_point_formats (len=2)
Extension: session_ticket (len=0)
Extension: application_layer_protocol_negotiation (len=14)
Extension: status_request (len=5)
Extension: signature_algorithms (len=20)
Extension: signed_certificate_timestamp (len=0)
Extension: key_share (len=43)
Extension: psk_key_exchange_modes (len=2)
Extension: supported_versions (len=11)
Extension: compress_certificate (len=3)
Extension: Reserved (GREASE) (len=1)
Extension: padding (len=202)
作为“礼尚往来”,服务器收到“Client Hello”后,会返回一个“Server Hello”消息。
把版本号对一下,也给出一个随机数(Server Random),
然后从客户端的列表里选一个作为本次通信使用的密码套件,在这里它选择了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 112
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 108
Version: TLS 1.2 (0x0303)
Random: 306ba0a553a042f119e52473b67c6e293f3ee61012fff8ff…
Session ID Length: 32
Session ID: 27e4112dd0e1299b7fd853c7787c16308bf03435a620a8fd…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Compression Method: null (0)
Extensions Length: 36
Extension: renegotiation_info (len=1)
Extension: server_name (len=0)
Extension: ec_point_formats (len=4)
Extension: extended_master_secret (len=0)
Extension: application_layer_protocol_negotiation (len=11)
TLSv1.2 Record Layer: Handshake Protocol: Certificate
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 770
Handshake Protocol: Certificate
Handshake Type: Certificate (11)
Length: 766
Certificates Length: 763
Certificates (763 bytes)
Certificate Length: 760
Certificate: 308202f4308201dca003020102020900fa9c5b27a0c1368d… (id-at-commonName=www.chrono.com)
signedCertificate
algorithmIdentifier (sha256WithRSAEncryption)
Padding: 0
encrypted: 6dd90318e47d1b41728f04802a85eedca00a615feed7a67f…
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 300
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 296
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey Length: 32
Pubkey: 780f25b44d2ebb5df7ead5e9b6b32c2d40bb7094f16ac7c6…
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: 6778592c6742cbd45ff120ce72d1a442a8c5cdbb65e792d5…
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 4
Handshake Protocol: Server Hello Done
Handshake Type: Server Hello Done (14)
Length: 0
然后,服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。
接下来是一个关键的操作,因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params),用来实现密钥交换算法,再加上自己的私钥签名认证
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 300
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 296
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey Length: 32
Pubkey: 780f25b44d2ebb5df7ead5e9b6b32c2d40bb7094f16ac7c6…
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: 6778592c6742cbd45ff120ce72d1a442a8c5cdbb65e792d5…
-
1.指明自己使用的椭圆曲线(一般根据客户端的拓展中supported_groups中的选择椭圆曲线算法)
-
2 服务器本地计算一个大数(BIGNUM),乘上曲线的base point,得到一个新的point,这个point就是公钥,用04+x+y的格式组织起来。04表示unconpressed point,和客户端的ec_point_formats有关。
-
3 签名 。和RSA握手不同,RSA情况下, 只要能值正常协商密钥,那么必然服务器端有证书对应的私钥,也间接表明了服务器拥有该证书。
DHE/ECDHE不同,证书对应的私钥并不参与密钥协商,如果要证明服务器拥有证书,则必然有签名的操作(就像双向认证的情况下,客户端需要发送certificate verify)。
被签名数据从curve type起,至point的y为止。
对于TLS1.2,签名算法使用client hello拓展中提供的摘要算法;TLS1.0和TLS1.1,如果本地证书是ECC证书,即若要使用ECDSA签名,这种摘要算法为SHA1,其他的情况摘要算法为md5+sha1。
计算摘要之后就调用RSA或者ECDSA进行签名。注意的是,TLS1.2时报文要带上2字节的“Signature Hash Algorithm”,如上图高亮部分,这是TLS1.2协议相较于之前协议不同之处之一,但是这2部分不参与签名计算
这相当于说:“刚才我选的密码套件有点复杂,所以再给你个算法的参数,和刚才的随机数一样有用,别丢了。为了防止别人冒充,我又盖了个章。
”之后是“Server Hello Done”消息,服务器说:“我的信息就是这些,打招呼完毕。”这样第一个消息往返就结束了(两个 TCP 包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params。
客户端这时也拿到了服务器的证书,那这个证书是不是真实有效的呢?
开始走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份:“刚才跟我打招呼的不是骗子,可以接着往下走。”然后,客户端按照密码套件的要求,也生成一个椭圆曲线的公钥(Client Params),用“Client Key Exchange”消息发给服务器。
Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。
至于具体的计算原理和过程,因为太复杂就不细说了,但算法可以保证即使黑客截获了之前的参数,也是绝对算不出这个随机数的。
现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。
用这三个作为原始材料,就可以生成用于加密会话的主密钥,叫“Master Secret”。而黑客因为拿不到“Pre-Master”,所以也就得不到主密钥。为什么非得这么麻烦,非要三个随机数呢?
这就必须说 TLS 的设计者考虑得非常周到了,他们不信任客户端或服务器伪随机数的可靠性,为了保证真正的“完全随机”“不可预测”,把三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测。
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
这里的“PRF”就是伪随机数函数,它基于密码套件里的最后一个参数,比如这次的 SHA384,通过摘要算法来再一次强化“Master Secret”的随机性。
主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。
有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。
意思就是告诉服务器:“后面都改用对称算法加密通信了啊,用的就是打招呼时说的 AES,加密对不对还得你测一下。
”服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了
- HTTPS 协议会先与服务器执行 TCP 握手,然后执行 TLS 握手,才能建立安全连接
- 握手的目标是安全地交换对称密钥,需要三个随机数,第三个随机数“Pre-Master”必须加密传输,绝对不能让黑客破解;
- “Hello”消息交换随机数,“Key Exchange”消息交换“Pre-Master”;
- “Change Cipher Spec”之前传输的都是明文,之后都是对称密钥加密的密文
TLS协议组成
记录协议
Record Protocol ,规定了TLS收发数据的基本单位。它有点类似TCP里的segment,所有的其它子协议都需要通过记录协议发出
但是多个记录数据可以在1个TCP包里一次性发出,也并不需要像TCP那样返回ACK
警报协议
Alert protocol 职责是向对方发出警报信息,类似http协议里的状态码。
比如protocol_version 就死不支持旧版本 bad_certificate就是证书有问题
收到警报后,另一方可以选择终止连接,也可以选择继续
握手协议
(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商
- TLS 版本号、
- 随机数、
- 密码套件等信息,
然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统
变更密码规范协议
(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的
第一阶段:C/S两端共享Client Random、Server Random 和 Server Params信息
客户端--->服务器:
客户端的版本号、支持的密码套件,还有一个随机数(Client Random)
服务端--->客户端:
客户端的版本号、选择的客户端列表的密码套件如:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384、随机数随机数(Server Random)
服务端--->客户端:
服务端证书(Server Certificate)
服务端--->客户端:
发送Server Key Exchange类型的请求,携带椭圆曲线的公钥(Server Params)用以实现密钥交换算法,另附私钥签名
服务端--->客户端:
发送完毕
第二阶段:证书验证
前验条件:客户端证书链逐级验证、证书公钥验证签名,服务端身份验证成功(证书合法)
客户端--->服务端
发送Client Key Exchange类型的请求,携带椭圆曲线的公钥(Client Params)用以实现秘钥交换算法
第三阶段:主密钥生成
客户端、服务端分别使用Client Params、Server Params通过ECDHE算法计算出随机值pre-master,然后用
Client Random、Server Random 和 Pre-Master三个值作为原材料,用PRF伪随机数函数(利用密码套件的摘要算法再次强化结果
值maser secert的随机性)计算出主密钥Master Secret,
主密钥并不是会话秘钥,还会再用PRF扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)
客户端--->服务端:
客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证.
服务端--->客户端:
服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束.
1.client hello 【client->server】
- client发送 随机数randomC,tls版本,支持的密码套件,扩展信息到服务端
2.server hello 【server-client】
- server 发送 随机数randomS,tls版本确认,选择的密码套件,扩展信息到client
3.certificate 【server->client】
4.server key exchange 【server->client】
- EC Diffie-Hellman Server Params
- Curve Type(曲线类型)
- Pubkey
- Signature Algorithm和Signature