网络安全篇,面对复杂多变的网络环境,我们需要掌握哪些关于网络安全的相关知识,聊一聊与网络安全相关的:HTTPS、SSL、TLS 等。
网络安全专题
- 网络安全的基石
《网络安全 — HTTPS》
《网络安全的基石(上)— 加密》
《网络安全的基石(下)— 完整性与身份认证》
《公钥信任问题 — 数字证书与 CA》
《信任始于握手 — TLS 连接过程详解》
- HTTPS 的优化
《TLS 1.3 特性解析》
《如何优化 HTTPS 连接》- 待完善
TLS 协议的组成
在介绍 TLS 握手之前,我们先来简单介绍下 TLS 协议的组成。
TLS 实际由若干不同职责的模块组成,比较常用的有握手协议、变更密码规范协议、警报协议和记录协议等。
从体系结构图可以看出,SSL/TLS 协议可分为两层:握手协议和记录协议。
握手协议
握手协议用来协商会话参数(如会话密钥、应用层协议等)。它实际由若干不同职责的子模块组成,比较常用的有握手协议、变更密码规范协议、警报协议和记录协议等。
- 握手协议(Handshake Protocol)
建立在 SSL 记录协议之上,是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多。用于在实际传输数据之前,通讯双方进行身份认证、协商加密算法、交换会话密钥,用于后续的混合加密系统。
- 变更密码规范协议(Change Cipher Spec Protocol)
是一个非常简单的“通知”。用于告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。为了保障 SSL 传输过程的安全,客户端和服务器双方应该每间隔一段时间改变加密规范。在客户端和服务器完成握手协议之后,它需要向对方发送相关消息,通知对方随后的数据将使用已协商的加密规范进行保护。
- 警报协议(Alert Protocol)
职责是向对方发出警报信息,类似于 HTTP 协议里的状态码。比如 protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。警报协议(Alert Protocol)的职责是向对方发出警报信息,类似于 HTTP 协议里的状态码。比如 protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。
记录协议 (Record Protocol)
建立在可靠的传输协议(如 TCP)之上,为高层协议提供数据封装、压缩及加密等基本功能的支持。保证连接的机密性和完整性。所有的其他子协议都需要通过记录协议发出。但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK。
有关 SSL 协议体系结构的更详细介绍你可以参考这篇文章。
SSL/TLS 在网络模型中的位置
你是否有想过?在网络通信模型中, TLS 协议是处在什么位置呢?下面我们通过一张图来简单了解下:
SSL/TLS 在网络模型中属于应用层协议(实际在 OSI 的会话层),接管应用层的数据加解密,并通过网络层进行收发。
TLS 握手过程
下图简要描述了 TLS 的握手过程,其中每个“框”都是一个记录,多个记录组成一个 TCP 包发送。所以,最多要经过 2 RTT(4 个消息)才可以完成握手,然后就可以在安全的通信环境里发送 HTTP 报文,实现 HTTPS 协议。
TLS 握手总是以 ClientHello 消息开始,就跟 TCP 握手总是以 SYN 包开始一样。
上面是相对简要的握手过程,实际上关于 TLS 握手过程可以划分为两种方式:使用 RSA 做密钥交换和 ECDHE 做密钥交换。下面我们就详细地介绍下它们的握手过程。
ECDHE 握手
在 TCP 成功建立连接之后,客户端首先发送一个 “Client Hello” 消息,也就是跟服务器“打招呼”。
内容包括:客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。
Handleshake Protocol: Client Hello
Version: TLS 1.2(0x0303)
Random:1cbf803321fd26234ds78sleds...
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 消息携带的相关信息,关键的随机数需要保留着。
作为“礼尚往来”,服务器收到 “Client Hello” 后,会返回一个 “Server Hello” 消息。把 TLS 版本对一下,也会返回一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件,如下选择了 “TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。
Handshake Protocol: Server Hello
Version: TLS 1.2(0x0303)
Random: 0e6320f21bae50842e96…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
这个意思是:把版本好对上了,服务端在客户端支持的密码套件中选择了一个合适的:椭圆曲线 + RSA、AES、SHA384。服务端也返回一个随机数,需要客户端保留着。
然后,服务端为了证明自己的身份,就把证书也发给客户端(Server Certificate 消息)。
接下来的关键操作,因为服务器选择了 ECDHE 算法,所以它会在证书发送后发送 “Server Key Exchange” 消息,里面是椭圆曲线的公钥(Server Params),用来实现秘钥交换算法,再加上自己的私钥签名认证。
Handshake Protocol: Server Key Exchange
EC Diffie-Hellman Server Params
Curve Type: named_cure (0x03)
Named Curve: x25519 (0x001d)
Pubkey: 3b39deaf00217894e...
Signature Algorithm: rea_pkcs1_sha512 (0x0601)
Signature: 37141adac38ea4...
这一步相当于:服务端又给了一个算法参数,和刚才的随机数一样需要客户端保留,为了防止别人冒充,服务端用私钥进行签名。
之后的 “Server Hello Done” 消息,服务端的“打招呼”完毕。
此时第一个 RTT 就结束了(2 个 TCP 数据包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params。
客户端也拿到了服务器的证书,那这个证书里的公钥是否真实有效呢?回想下我们在上期《公钥信任问题 — 数字证书与 CA》介绍的证书验证过程,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份。
然后,客户端按照密码套件的要求,也生成一个椭圆曲线的公钥(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 的设计考虑的非常全面,为了保证真正的“完全随机和不可预测”,他们不信任客户端或服务器的伪随机数的可靠性,所以采用三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测。
生成的主密钥为 48 字节,但它还不是最终用于通信的会话密钥,还会再用主密钥派生出会话密钥。到这一步 TLS 的握手就要结束了。客户端发送一个 “Change Cipher Spec”,然后再发一个 “Finished” 消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。
服务器也是同样的操作,发送 “Change Cipher” 和 “Finished” 消息,双方验证加密解密 OK,握手正式结束,后面就开始收发被加密的 HTTP(Application Data)请求和响应了。
RSA 握手过程
如今主流的 TLS 握手就是 ECDHE,整个过程可以说是够复杂的。不过整个握手过程好像和其他的地方看到的不一样呢?这与传统的握手有 2 点不同:
使用 ECDHE 实现秘钥交换,而不是 RSA,所以会在服务端发出“Server Key Exchange” 消息。
因为使用了 ECDHE,客户端可以不用等到服务器发回 “Finished” 确认握手完毕,就立即发出 HTTP 报文了,节省了一个 RTT 时间。这个叫做 “TLS False Start”,意思就是“抢跑”,和 “TCP Fast Open” 有点像,都是不等连接完全建立就提前发送应用数据来提高传输效率。
下面我们看下传统的 TLS 握手过程:
大体流程没有变,只是 “Pre-Master”(预主密钥) 不再需要用算法生成,而是客户端直接生成随机数,然后用服务器的公钥,通过 “Client Key Exchange” 消息发给服务器。服务器再用私钥解密,这样双方也实现了共享三个随机数,就可以生成主密钥。
说道这里,TLS 的握手基本上就介绍完了。私钥可以说是整个 TLS/SSL 协议中最重要的东西了,结合上面介绍的握手过程,我们再总结下两种握手过程中私钥所起到的作用。
ECDHE 握手时的私钥用途
对于 ECDHE 来说,客户端和服务端双方交换的是椭圆曲线参数,私钥只是用来签名,这是为了保证消息是持有私钥者发送的,而非冒充的。双方交换完参数之后生成预主密钥,再生成主密钥和会话密钥(这里就和 RSA 后面的流程一样了)。
RSA 握手时的私钥用途
对于 RSA 来说,客户端生成预主密钥,然后用公钥加密再发送给服务器,服务器得到预主密钥,然后由预主密钥生成主密钥,再由主密钥生成会话密钥,最后用会话密钥来通信。
可以看出 RSA 和 ECDHE 密钥交换算法的私钥用途是不一样的,RSA 密钥交换时私钥用来解密,ECDHE 则是用来签名。
SSL/TLS 中预主密钥,主密钥和会话密钥
主密钥是有预主密钥、客户端随机数和服务端随机数通过 PRF 函数生成的;会话密钥是由主密钥、客户端随机数和服务器随机数听过 PRF 函数生成的,会话密钥里面包含对称加密密钥、消息认证和 CBC 模式的初始向量,但对于非 CBC 模式的加密算法来说,就没有用到这个初始向量。
session 缓存和 session ticket 里面保存的主密钥,而不是会话密钥,这是为了保证每次会话都是独立的,这样才能安全。即使一个主密钥泄露了也不影响其他会话。关于该部分将在后面的优化部分再展开介绍。
双向认证
大家平时在登录网上银行时,有时要使用 U 顿给用户颁发客户端证书。其实那就是双向认证,即服务端验证客户端身份。
但是在生活中的绝大多数场景下,我们只是“单向认证” 的握手过程,只认证了服务器的身份,而没有认证客户端的身份。这是因为通常单向认证通过后已经建立了安全通信,再配合账号密码等简单的手段就能够确认客户端的真实身份。
双向认证的流程也没有发生太大的变化,只是在 “Server Hello Done” 之后,“Client Key Exchange” 之前,客户端要发送 “Client Certificate” 消息,服务器收到后也把证书链走一遍,验证客户端的身份。
总结
今天我们主要介绍了 HTTPS/TLS 的握手,整个过程还是比较复杂的。不过为了方便记忆我们可以简单总结为如下几条:
HTTPS 协议在握手之前,需要先进行 TCP 握手,然后执行 TLS 握手,才能建立安全连接;
TLS 握手的目的是安全地交换对称密钥(会话密钥),这个过程需要三个随机数,第三个随机数 “Pre-Master” 必须加密传输,绝对不能让黑客破解;
“Hello” 消息交换随机数,“Key Exchange” 消息交换 “Pre-Master”;
“Change Cipher Spec” 消息之前传输的都是明文,之后都是通过对称密钥加密的密文。
网络安全涉及了方方面面太多的知识,尤其是网络的基础知识对我们来说还是非常重要的,关于这部分大家又有什么要分享的?欢迎你的分享留言或指正。
网络安全系列专题
- 网络安全 — HTTPS
- 网络安全的基石(上)— 加密
- 网络安全的基石(下)— 完整性与身份认证
- 公钥信任问题 — 数字证书与 CA
扩展阅读
- HTTPS中CA证书的签发及使用过程
- HTTPS中SSL/TLS握手时的私钥用途(RSA、ECDHE)