SSL/TLS 的基本思想是 公钥加密, 私钥解密(非对称加密). 客户端请求时, 服务端先将自己的公钥告诉它, 然后客户端使用公钥对内容加密, 服务端收到之后使用私钥解密.

但是有几个问题:

  1. 如何保证服务端的公钥不会被篡改. 因为如果请求服务端公钥的过程被拦截, 拦截者返回了一个不正确的公钥, 就会造成问题
  2. 我们必须具有服务器的公钥。但是, 我们无法存储地球上所有服务器的公钥, 该数据非常庞大, 并且如何保证公钥的修改同步问题
  3. 公钥加密, 私钥解密(非对称加密)的方式比较耗时, 如果每次数据交换都进行会增大整个请求的耗时

为了解决上面的问题, 引入了 数字证书SSL/TLS 握手:

  • 数字证书: 将公钥放在数字证书中, 只要证书通过客户端验证, 那公钥就是可信的; 数字证书每次由服务端在请求前发送给客户端, 解决了公钥同步问题
  • SSL/TLS 握手: 在每一次对话之前, 服务端和客户端通过握手生成一个密钥, 之后的数据都通过这个密钥使用对称加密方式加密, 就避免了每次使用非对称加密的耗时问题

如何验证证书有效

服务端的公钥放在数字证书中, 那客户端是如何验证这个证书的是不是正确的呢?

解决方案是证书颁发机构, 或简称为 CA (根证书机构)。当我们安装操作系统或浏览器时, 可能会附带一系列受信任的 CA。在这些 CA 列表中, 还存储了 CA 的公钥。

  • Mac OS: 可以在钥匙串中查看系统信任的证书
  • Android: /system/etc/security/cacerts 文件夹中存有系统信任的证书

比如, 当 Google.com 的服务器向我们发送其证书时, 它还会提到该证书是由 GeoTrust 签名的。如果我们信任 GeoTrust, 则可以使用 GeoTrust 的公钥验证 GeoTrust 是否确实签署了 Google.com 服务器发送给我们的证书。(CRL 或 OCSP)
如果我们要自己签署证书, 我们需要知道私钥, 但私钥只有 GeoTrust 知道。因此攻击者就无法自己签署证书并假装自己是 Google.com。而且证书被修改后, 即使只有一位, 签名也将不正确, 客户端将拒绝它。

OCSP 在线证书状态协议

  1. Alice 与 Bob 使用 Carol 颁发的数字证书。该场景中 Carol 是证书颁发机构(CA);
  2. Alice 向Bob 发送其由 Carol 颁发的数字证书,并发出请求创建连接的申请;
  3. Bob 担心 Alice 的私钥已经泄露,因此向 Carol 发送“OCSP请求”消息并包含 Alice 的数字证书序列号;
  4. Carol 的OCSP 响应端从 Bob 发送的消息中获取数字证书的序列号,并在 CA 数据库中查找该数字证书的状态;
  5. Carol 向 Bob 发送由其私钥加密的消息“OCSP响应”,并包含证书状态正常的信息;
  6. 由于 Bob 事先已经安装了 Carol 的数字证书,因此 Bob 使用 Carol 的公钥解密消息并获取到 Alice 的数字证书状态信息;
  7. Bob 验证通过后, Bob 决定与 Alice进行通信。

curl 查看握手大致过程

使用 curl 输出请求该网站首页的过程:

  • OUT 代表客户端发出请求
  • IN 代表服务端的响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
➜ curl -iIv https://blog.jiyang.site
* Connected to blog.jiyang.site (47.105.79.131) port 443 (#0)
* ALPN, offering h2
//ALPN (Application-Layer Protocol Negotiation TLS 的一个扩展)
//客户端可以在 client hello 消息中发送自己支持的应用层协议给服务端,
//这里发送的是 h2(http2) 和 http/1.1, 然后服务端将选择一个协议,
//并在 server hello 消息中发送到客户端. 这样便可以在一次的往返中确定将要使用的应用协议。
* ALPN, offering http/1.1
*(OUT), TLS handshake, Client hello (1)
//客户端通过向服务器发送客户端 hello 消息来启动会话.
*(IN), TLS handshake, Server hello (2)
//服务器以服务器 hello 消息响应
*(IN), TLS handshake, Certificate (11)
//服务器将其证书发送给客户端。服务器证书包含服务器的公钥,
//客户端将使用此密钥对服务器进行身份验证并加密 premaster secret
*(IN), TLS handshake, Server key exchange (12)
//这是一个可选步骤,其中服务器创建临时密钥并将其发送给客户端
*(IN), TLS handshake, Server finished (14)
//服务端 hello done
*(OUT), TLS handshake, Client key exchange (16)
//客户端使用两个随机值计算一个预密码后, 再使用服务端的公钥进行一次加密, 然后发起密钥改变
*(OUT), TLS change cipher, Client hello (1)
//通知服务器,将使用刚刚协商的密钥和算法对 “客户端完成” 消息之后的所有消息进行加密
*(OUT), TLS handshake, Finished (20)
//客户端握手结束
*(IN), TLS change cipher, Client hello (1)
//通知客户端服务器将开始使用刚刚协商的密钥对消息进行加密
*(IN), TLS handshake, Finished (20)
//该消息是使用会话密钥和MAC密钥到此为止整个交换的哈希。如果客户端能够成功解密此消息并验证包含的哈希,
//则可以确保 SSL/TLS 握手成功,并且客户端计算机上计算出的密钥与服务器上计算出的密钥匹配

Wireshark 查看握手过程

概览

高亮的部分就是 TLS 握手的过程

Client Hello Server Hello Server certificates Server Key exchange Client Key exchange Server final Respnse

Initial Client Message to Server

Client Hello

Client Hello 发送的消息:

属性 描述
Version TLS 1.0 客户端使用的 TLS 版本, Version 2 对应 SSL 2.0, version 3 对应 SSL 3.0,version 3.1 对应 TLS
Random 随机值是一个4字节的数字,由客户端的日期和时间加上一个 28 字节随机生成的数字组成,该数字最终将与服务器随机值一起使用,以生成一个 master screct,从 master screct 中可以导出加密密钥
Session ID 如果客户端有上一次会话的 session id 的话, 客户端可以携带上, 用它来恢复之前的会话. 因为创建新会话需要占用大量处理器资源来做公钥操作,可以通过恢复具有已建立会话密钥的现有会话来避免这种操作
Cipher Suites 客户端支持的加密算法
Compression Methods 客户端支持的压缩算法
application layer protocol negotiation - ALPN 协议

Server Response to Client

Server Hello

Server Hello 发送的消息:

属性 描述
Version TLS 1.0 服务端将选择服务端和客户端支持的最高版本
Random 服务端产生的随机值, 产生方法和客户端一样
Session ID 服务端为这次会话分配的 id, 有 3 种情况:
1. New Session ID: 如果客户端没有在 Client Hello 消息中携带 Session ID 的话,会生成一个新的; 即使 Client Hello 消息携带了 SessionID, 也有可能服务端已经不能恢复上一次会话了, 这种情况也会生成一个新的 Session ID
2. Resumed Session ID: Client Hello 携带了 Session ID 且服务器会恢复它对应的会话, 服务器就会返回和 Client Hello 携带上来一样的 Session ID
3. Null: 一个新会话,但是服务器不愿意在以后恢复它,因此不会返回任何ID
Cipher Suite 服务端从客户端支持的加密算法中选择使用的一个
Compression Methods 服务端的压缩算法
application layer protocol negotiation ALPN 协议

Server certificates

服务端发送自己的证书: 这里发送了两个证书,一个是网站自己的证书,另一个是网站证书申请使用的二级证书。因为网站的证书不是直接从根证书机构签发的, 所以这里会发送中级颁发机构的证书.
客户端先通过本地的根证书验证中级颁发机构的证书,然后再使用中级颁发结构的证书验证网站的证书。客户端验证证书通过后,会从网站证书中提取公钥.

  • blog.jiyang.site: 网站证书
  • Let’s Encrypt Authority X3: 中级颁发机构证书

Server Key Exchange

这里服务端在一次通信里做了 2 件事:

  • Server Key Exchange
  • Server Hello Done

Server Key Exchange: 是一个可选步骤,服务器创建一个临时密钥并将其发送给客户端。客户端可以使用此密钥在此过程的后面加密 Client Key Exchange 消息。仅当公共密钥算法未提供加密客户端密钥交换消息所需的密钥材料时(例如,服务器的证书不包含公共密钥时),才需要执行此步骤

Server Hello Done

此消息表明服务器握手已完成,正在等待客户端的响应

Client Response to Server

Client Key Exchange

这里客户端在一次通信里做了 3 件事:

  • Client Key Exchange

客户端使用两个随机值计算出premaster secret后, 发送 Client Key Exchange message. 在将 premaster secret传输到服务器之前, 已使用服务器证书中的公钥对其进行了加密. 双方将在本地计算 master secret 并从中获取会话密钥.

如果服务器可以解密此数据并完成协议, 则可以向客户端保证服务器具有正确的私钥. 这一步对于证明服务器的真实性至关重要. 只有具有与证书中的公钥匹配的私钥的服务器才能解密此数据并继续协议下面的步骤.

此消息还将包括协议版本. 服务器将验证它是否与客户端问候消息中发送的原始值匹配. 此措施可防止回滚攻击. 回滚攻击通过操纵消息来工作, 以使服务器和客户端使用安全性较低的协议的较早版本.

  • Change Cipher Spec

该消息通知服务器, 将使用刚刚协商的密钥和算法对Client Finished message之后的所有消息进行加密.

  • Encrypted Handshake Message

这个报文的目的就是告诉对端自己在整个握手过程中收到了什么数据, 发送了什么数据. 来保证中间没人篡改报文. 其次, 这个报文作用就是确认秘钥的正确性. 因为 Encrypted handshake message 是使用对称秘钥进行加密的第一个报文, 如果这个报文加解密校验成功, 那么就说明对称秘钥是正确的.

Server Final Response

接着服务端又在一次通信里做了 2 件事:

  • Change Cipher Spec

此消息通知客户端服务器将开始使用刚刚协商的密钥来加密消息。

  • Encrypted Handshake Message

和 Client 上面的 Encrypted Handshake Message 目的一样