为什么要有 HTTPS
为什么要有 HTTPS?简单的回答是:“因为 HTTP 不安全”。HTTP 怎么不安全呢?
- 通信的消息会被窃取,无法保证机密性(保密性):由于 HTTP 是 “明文” 传输,整个通信过程完全透明,其他人能够窃取到传输的明文信息。
- 通信的消息会被篡改,无法保证完整性:使用 HTTP 通信,任何人都能够在通信的过程中截获并篡改请求报文、响应报文,但消息接收者无法识别报文是否被篡改。
- 通信的消息会被伪造,无法确认消息发送者的真实身份(身份认证):使用 HTTP 通信,任何人都能够给一个接收者发送消息,但是消息接收者无法确认消息发送者的真实身份。也就是说,无法进行身份认证。
- 无法保证不可否认性(不可抵赖):使用 HTTP 通信,消息接收者收到一个消息后,通信的一方可以否认(抵赖)消息不是他发送的。也就是说,消息接收者无法证明这一消息的确是由通信的一方发送的。
只有同时具备了机密性、完整性、身份认证、不可否认这四个特性,才能算得上是安全的通信。
HTTPS 使用 HTTP 进行通信,并使用 SSL/TLS 为 HTTP 增加了机密性、完整性、身份认证、不可否认这四大安全特性。
介绍 SSL/TLS
SSL 即安全套接层(Secure Sockets Layer)。
SSL 发展到 v3 时已经证明了它是一个非常好的安全通信协议,于是互联网工程组 IETF 在 1999 年把 SSL 改名为 TLS(传输层安全,Transport Layer Security),正式标准化,版本号从 1.0 重新算起,所以 TLS1.0 实际上就是 SSLv3.1。
至今,TLS 已经发展出了三个版本,分别是 2006 年的 1.1、2008 年的 1.2 和 2018 的 1.3,每个新版本都紧跟密码学的发展和互联网的现状,持续强化安全和性能。目前应用最广泛的 TLS 版本是 1.2,而之前的版本(TLS1.1 / 1.0、SSLv3 / v2)都已经被认为是不安全的。
TLS 综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术。非对称加密在这个场景中发挥的作用是 “密钥协商”,通信的双方协商得到会话密钥。会话密钥用于 HTTP 报文的加解密,以实现机密性。
TLS 由记录协议、握手协议、警报协议、变更密码规范协议、扩展协议等几个子协议组成:
- **记录协议(Record Protocol)**规定了 TLS 收发数据的基本单位是:记录(record)。它有点像是 TCP 里的 segment,所有其他的子协议都需要通过记录协议发出。多个记录数据可以在一个 TCP 包里一次性发出。
- **握手协议(Handshake Protocol)**是 TLS 里最复杂的子协议。通信的双方在 TLS 握手的过程中协商 TLS 的版本号、密码套件,交换随机数、数字证书和密钥参数,最终通信的双方协商得到会话密钥。
- **警报协议(Alert Protocol)**的职责是向对方发出警报信息。收到警报的一方可以选择继续连接,也可以立即终止连接。它有点像是 HTTP 里的状态码,比如 protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题。
- **变更密码规范协议(Change Cipher Spec Protocol)**就是一个“通知”,告诉对方:后续传输的都是对称密钥加密的密文。也就是说,“Change Cipher Spec” 消息之前传输的都是明文,之后传输的都是对称密钥加密的密文。
TLS 的密钥套件
通信的双方在 TLS 握手的过程中会协商使用的密码套件(cipher suite,也被称为加密套件)。
TLS 的密码套件命名非常规范。固定的格式是:“密钥交换算法 + 签名算法 + 对称加密算法 + 分组模式 + 消息摘要算法”,例如 “ECDHE-RSA-AES256-GCM-SHA384” 密码套件的意思就是:
-
**密钥交换算法使用的是:ECDHE。**通信的双方在 TLS 握手的过程中协商 TLS 的版本号、密码套件,交换随机数、数字证书和密钥参数,通信的双方使用 ECDHE 算法算法 "Pre Master Secret"。
-
**签名算法使用的是:RSA。**TLS 握手的过程中,使用 RSA 签名算法进行数字签名。服务器给浏览器发送 "Server Key Exchange" 消息,服务器对密钥参数(消息中的 Public 参数)进行数字签名,然后把签名值一并发送给浏览器。浏览器收到 "Server Key Exchange" 消息后,使用数字证书中的公钥对密钥参数(消息中的 Public 参数)进行验签。
-
对称加密算法使用的是:AES-256。TLS 握手后的通信使用 AES 对称加密算法进行加密,以实现机密性。对称密钥的长度 256 位
-
**分组模式使用的是:GCM。**加密明文的长度不固定,而一次对称加密只能处理特定长度的一块数据,这就需要进行迭代,以便将一段很长的数据全部加密,而迭代的方法就是分组模式。
-
**消息摘要算法使用的是:SHA-384。**TLS 握手的过程中,两次用到了该消息摘要算法。一次是:“PRF” 通过消息摘要算法,根据三个随机数(Client Random、Server Random 和 Pre Master Secret)计算主密钥 "Master Secret" 的值。另一次是:通信的双方给对方发送 "Finished" 消息。一方对之前发送的数据做摘要,再使用会话密钥对摘要进行对称加密,让对方进行验证(类似数字签名的验签)。TLS 握手后的通信使用该消息摘要算法进行消息认证,防止消息被篡改。
TLS1.2 握手的过程
使用 HTTPS 协议通信,通信的双方会先建立 TCP 连接,然后执行 TLS 握手,之后就可以在安全的通信环境里收发 HTTP 请求和响应了。
执行 TLS 握手的目的是:通信的双方安全的协商会话密钥。会话密钥用于 HTTP 报文的加解密,以实现机密性。
TLS1.2 握手可以划分为两种方式:使用 RSA 算法实现密钥交换 和 使用 ECDHE 算法实现密钥交换。
下面说的是使用 ECDHE 算法实现密钥交换的 TLS 握手过程。
TLS 握手过程的简要描述:通信的双方在 TLS 握手的过程中协商 TLS 的版本号、密码套件,交换随机数、数字证书和密钥参数,最终通信的双方协商得到会话密钥。"Hello" 消息交换随机数,"Key Exchange" 消息交换密钥参数。
- 浏览器给服务器发送 "Client Hello" 消息,服务器给浏览器发送 "Server Hello" 消息。通信的双方协商 TLS 的版本号、密码套件,并交换随机数。
- 交换数字证书:服务器为了向浏览器证明自己的身份,服务器给浏览器发送 "server Certificate" 消息,以发送数字证书链,其中包含了两个证书。一个是 CA 机构颁发的数字证书,另一个是 CA 机构的数字证书。
- 服务器给浏览器发送 "Server Key Exchange" 消息,浏览器给服务器发送 "Client Key Exchange" 消息。通信的双方交换密钥参数(Client Params 和 Server Params),然后通信的双方使用 ECDHE 算法算出 "Pre Master Secret"。
- 通信的双方根据自己已知的参数(Client Random、Server Random 和 Pre Master Secret)算出主密钥 "Master Secret",并使用主密钥拓展出更多的密钥(会话密钥),避免只用一个密钥带来的安全隐患。
- 浏览器给服务器发送 "Change Cipher Spec" 消息,服务器也给浏览器发送 "Change Cipher Spec" 消息。告诉对方:后续传输的都是对称密钥加密的密文。
- 浏览器给服务器发送 "Finished" 消息,服务器也给浏览器发送 "Finished" 消息。通信的双方把之前发送的数据做摘要,再使用会话密钥对摘要进行对称加密,让对方进行验证(类似数字签名的验签)。
- 双方都验证成功,握手正式结束,之后就可以正常收发被加密的 HTTP 请求和响应了。
TLS 握手的过程如下图所示,其中每一个框都是一个记录,多个记录组合成一个 TCP 包发送。所以,最多经过两次消息往返(4 个消息,或者说 4 个 TCP 包)就可以完成 TLS 握手。
第一个消息往返
浏览器与服务器建立 TCP 连接之后,浏览器会首先给服务器发送 "Client Hello" 消息。"Client Hello" 消息携带几个参数:Version、Random、Cipher Suites。
- Version:TLS 的版本号
- Random:浏览器生成的随机数(Client Random)。随机数用于后续生成主密钥
- Cipher Suites:浏览器支持的密码套件
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Random: 1cbf803321fd2623408dfe…
Cipher Suites (17 suites)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
服务器收到浏览器发送的 "Client Hello" 消息后,服务器给浏览器发送 "Server Hello" 消息。"Server Hello" 消息携带几个参数:Version、Random、Cipher Suite。
- Version:TLS 的版本号
- Random:服务器生成的随机数(Server Random)。随机数用于后续生成主密钥
- Cipher Suite:服务器选择使用的密码套件(服务器从浏览器发送的 "Client Hello" 消息的 Cipher Suites 参数中选)
Handshake Protocol: Server Hello
Version: TLS 1.2 (0x0303)
Random: 0e6320f21bae50842e96…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
服务器为了向浏览器证明自己的身份,服务器给浏览器发送一个证书链,包含了两个证书。一个是 CA 机构颁发的数字证书,另一个是 CA 机构的数字证书。
通过引入数字证书,实现了服务器的身份认证功能,这样即便黑客伪造了服务器,但是由于数字证书是没有办法伪造的,所以黑客依然无法欺骗用户。
由于服务器选择了使用 ECDHE 密钥交换算法,因此服务器需要给浏览器发送【服务端的椭圆曲线的公钥】(Server Params)。为了防止公钥被第三方篡改、被黑客冒充,服务器会对公钥进行数字签名,然后把签名值一并发送给浏览器。浏览器收到后,使用数字证书中的公钥对密钥参数(消息中的 Public 参数)进行验签。
服务器给浏览器发送 "Server Key Exchange" 消息。"Server Key Exchange" 消息携带几个参数:Curve Type、Named Curve、Pubkey、Signature Algorithm、Signature。
- Curve Type、Named Curve:使用的椭圆曲线的类型
- Pubkey:根据【服务端的椭圆曲线的私钥】和 基点 G 计算出的【服务端的椭圆曲线的公钥】(Server Params),用于后续计算 “Pre Master Secret”
- Signature Algorithm、Signature:使用的签名算法、签名值
Handshake Protocol: Server Key Exchange
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey: 3b39deaf00217894e...
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature: 37141adac38ea4...
服务器给浏览器发送 "Server Key Exchange" 消息之后,服务器发送信息完毕。服务器给浏览器发送 "Server Hello Done" 消息,服务器向浏览器说明:服务器发送信息完毕了。
至此,第一个消息往返就结束了(2 个消息,或者说 2 个 TCP 包)。结果是:通信的双方通过明文传输共享了如下信息:浏览器生成的随机数(Client Random)、服务器生成的随机数(Server Random)、使用的椭圆曲线的类型、服务端的椭圆曲线的公钥(Server Params)。
验证数字证书
浏览器收到服务器发送的数字证书之后,浏览器需要验证数字证书是否合法、有效。
浏览器验证完数字证书合法、有效后,浏览器再用数字证书中的公钥验证【服务器给浏览器发送的 "Server Key Exchange" 消息】的签名,以确认服务器的身份。
浏览器确认了服务器的身份之后,就开始了第二个消息往返。
第二个消息往返
由于通信的双方协商的使用 ECDHE 密钥交换算法。因此浏览器需要给服务器发送【客户端的椭圆曲线的公钥】(Client Params)。浏览器给服务器发送 "Client Key Exchange" 消息。
Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
至此,通信的双方都获取到了 ECDHE 密钥交换算法需要的两个参数(Client Params、Server Params),于是通信的双方就使用 ECDHE 算法算出一个随机数,这个随机数被叫做 "Pre Master Secret"。
ECDHE 算法可以保证:即使黑客截获了之前的参数,黑客也绝对算不出这个 “Pre Master Secret” 随机数,因为 "Pre Master Secret" 的计算还使用了椭圆曲线的私钥。
目前,通信的双方已知三个随机数:Client Random、Server Random 和 Pre Master Secret。通信的双方使用这三个随机数作为原始信息,通过 PRF 算出主密钥(Master Secret)。因为黑客拿不到 "Pre Master Secret",所以黑客也就无法得到主密钥。
主密钥 "Master Secret" 的计算:这里的 “PRF” 就是伪随机数函数,它基于密码套件里的最后一个参数(消息摘要算法),比如 SHA384。“PRF” 通过 SHA384 消息摘要算法再一次强化 "Master Secret" 的随机性。
master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)
主密钥的长度为 48 字节,但是主密钥并不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如浏览器发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等,避免只用一个密钥带来的安全隐患。有了主密钥和派生的会话密钥,TLS 握手就快结束了。
浏览器给服务器发送 "Change Cipher Spec" 消息。浏览器告诉服务器:浏览器后续传输的都是对称密钥加密的密文。浏览器给服务器发送 "Finished" 消息。浏览器对之前发送的数据做摘要,再使用会话密钥对摘要进行对称加密,让服务器进行验证(类似数字签名的验签)。
服务器也进行和浏览器同样的操作。
- 服务器给浏览器发送 "Change Cipher Spec" 消息。服务器告诉浏览器:服务器后续传输的都是对称密钥加密的密文。
- 服务器给浏览器发送 "Finished" 消息。浏览器对之前发送的数据做摘要,再使用会话密钥对摘要进行对称加密,让浏览器进行验证。
双方都验证成功,握手正式结束,之后就可以正常收发被加密的 HTTP 请求和响应了。
使用 RSA 的 TLS 握手
TLS1.2 握手可以划分为两种方式:使用 RSA 算法实现钥交换 和 使用 ECDHE 算法实现密钥交换。
上面说了【使用 ECDHE 算法实现密钥交换的 TLS 握手过程】,下面说【使用 RSA 算法实现密钥交换的 TLS 握手过程】。
TLS 握手过程的简要描述:通信的双方在 TLS 握手的过程中协商 TLS 的版本号、密码套件,交换随机数、数字证书和密钥参数,最终通信的双方协商得到会话密钥。"Hello" 消息交换随机数,"Key Exchange" 消息交换 "Pre Master Secret"。
- 浏览器给服务器发送 "Client Hello" 消息,服务器给浏览器发送 "Server Hello" 消息。通信的双方协商 TLS 的版本号、密码套件,并交换随机数。
- 交换数字证书:服务器为了向浏览器证明自己的身份,服务器给浏览器发送 "server Certificate" 消息,以发送数字证书链,其中包含了两个证书。一个是 CA 机构颁发的数字证书,另一个是 CA 机构的数字证书。
- 服务器给浏览器发送 "Server Hello Done" 消息,服务器向浏览器说明:服务器发送信息完毕了。
- 浏览器验证数字证书合法、有效后,获取数字证书中【服务器的公钥】。浏览器生成随机数 "Pre Master Secret",使用【服务器的公钥】对生成的 "Pre Master Secret" 进行加密。然后,浏览器给服务器发送 "Client Key Exchange" 消息,把加密后的 "Pre Master Secret" 发送给服务器。
- 服务器收到浏览器发送的 "Client Key Exchange" 消息后,使用【服务器的私钥】解密出随机数 "Pre Master Secret"。
- 通信的双方根据自己已知的参数(Client Random、Server Random 和 Pre Master Secret)算出主密钥 "Master Secret",并使用主密钥拓展出更多的密钥(会话密钥),避免只用一个密钥带来的安全隐患。
- 浏览器给服务器发送 "Change Cipher Spec" 消息,服务器也给浏览器发送 "Change Cipher Spec" 消息。告诉对方:后续传输的都是对称密钥加密的密文。
- 浏览器给服务器发送 "Finished" 消息,服务器也给浏览器发送 "Finished" 消息。通信的双方把之前发送的数据做摘要,再使用会话密钥对摘要进行对称加密,让对方进行验证(类似数字签名的验签)。
- 双方都验证成功,握手正式结束,之后就可以正常收发被加密的 HTTP 请求和响应了。
【使用 RSA 算法实现钥交换的 TLS 握手过程】的大体流程没有变,只是 "Pre Master Secret" 不再需要通信的双方根据密钥参数使用算法算出,而是由浏览器生成,浏览器再使用服务器的公钥对生成的 "Pre Master Secret" 进行加密。然后,浏览器给服务器发送 "Server Key Exchange" 消息,把加密后的 "Pre Master Secret" 发送给服务器。服务器收到浏览器发送的 "Server Key Exchange" 消息后,使用服务器的私钥解密出随机数 "Pre Master Secret"。
这样通信的双方也已知三个随机数:Client Random、Server Random 和 Pre Master Secret,就可以生成主密钥了。
参考资料
23 | HTTPS是什么?SSL/TLS又是什么? (geekbang.org)