前言
说起网络通信协议,相信大家对 TCP 和 HTTP 都很熟悉,它们可以说是当今互联网通信的基石。但是,在网络安全方面,它们却是有着很大安全风险:
- 窃听风险。第三方攻击者可以随意窃听通信内容,比如获取支付账号密码。
- 冒充风险。第三方攻击者可以冒充他人身份与你通信,比如冒充银行网站以窃取银行账号密码。
- 篡改风险。第三方攻击者可以随意修改通信内容,比如在响应上加入钓鱼网址。
为此,SSL/TLS 协议应运而生。SSL/TLS 是建立在传输层之上、应用层之下的安全通信协议,它主要的设计意图就是消除上述几种安全风险,保证网络通信安全。我们熟知的 HTTPS 就是 HTTP + SSL/TLS 构建的,可以说 SSL/TLS 是当今互联网安全通信的基石。
那么,现在假如让你来设计 SSL/TLS 协议,你会怎么设计呢?
本文将从设计者的视角介绍如何一步步设计出一个简易版的 SSL/TLS 的过程,在文章的最后,再简单介绍 TLS 1.2 版本的工作机制,以此帮助大家对 SSL/TLS 协议的基本原理有一个更深入的理解。
基于对称加密算法的数据加密
窃听风险主要是因为通信双方在网络上明文传输数据,导致攻击者可以通过简单网络抓包就能获取到通信的内容。
要解决窃听风险,就最好的方法就是对数据进行加密。也即客户端在把数据发送出去之前,先对数据进行加密;服务端收到密文之后,再进行解密还原数据。这样就能避免在网络上传播明文,从而可以防止第三方攻击者的窃听。
提到加密算法,很多人首先会想到对称加密算法,它以简单和高效著称。对称加密指的是加密和解密都使用同一份密钥,常见的算法有 DES、AES 等。
现在,我们试着使用对称加密算法来实现安全通信:
使用对称密钥加密的前提是,通信双方都必须用同一份密钥来对数据进行加密。主要有线下和线上密钥交换两种方案可以达到该目的:
- 线下密钥交换,也即通信双方线下约定好当面交换密钥(比如通过U盘作为媒介)。该方案可以保证密钥交换的安全性,但是很难推广使用。因为在绝大多数场景中,客户端和服务端都不可能碰面。
- 线上密钥交换,也即通过网络来传输密钥。但在网络明文传输密钥同样也会被攻击者拦截,这样的加密也没有意义了。
因此,单纯的对称加密并不能满足通信安全的要求,我们还要继续优化......
基于非对称加密算法的数据加密
非对称加密算法指的是加密和解密使用不同的密钥,这两个不同的密钥组成一个密钥对,也即公钥和私钥。公钥是公开的密钥,所有人都能获取到;私钥则是保密的。当我们使用公钥对数据进行加密后,只有对应的私钥才能完成解密。常见的非对称加密算法有 RSA、ECC 等。
现在,我们试着使用非对称加密算法来实现安全通信:
通过非对称加密算法,我们既能实现对数据的加密,又能解决密钥交换的问题,从而消除了窃听风险。但是,非对称加密算法最大的缺点,就是加解密速度很慢,相比于对称加密算法要慢1000多倍。因此,非对称加密算法通常只适用于对少量数据的加密。
到目前为止,单纯地使用对称加密算法或非对称加密算法都无法满足要求,还需要继续优化......
基于对称加密+非对称加密算法的数据加密
既然对称加密算法加解密速度快,但存在密钥交换的问题;而非对称加密算法可以解决密钥交换问题,但加解密速度慢。那么我们可以把两种算法结合起来,也即通过对称加密算法进行数据加密,在交换对称密钥时,使用非对称加密算法来加密对称密钥,确保密钥在网络传输过程中不会被攻击者窃听。
现在,我们试着使用对称加密+非对称加密算法来实现安全通信:
使用对称加密+非对称加密算法的方案,我们消除了窃听风险,也不会存在加解密性能问题,但是还是无法消除冒充风险。
考虑如下场景:
- 攻击者把服务端的公钥拦截,并保存下来。
- 攻击者伪造成服务端,把自己的公钥发送给客户端。
- 攻击者拦截使用非法公钥加密后的对称密钥,解密后得到对称密钥明文,并保存下来。
- 攻击者使用服务端公钥重新加密对称密钥,伪造成客户端发送给服务端。
这番操作后,攻击者就能在客户端和服务端都不知情的情况下,得到了对称密钥。在这种场景下,攻击者从被动攻击的窃听,转为主动攻击的冒充,让客户端和服务端都误以为一直在跟对方通信。
因此,我们需要找到一种方法,让客户端能够确保自己收到的公钥,一定是真实的服务端发送过来的,也即能够认证“服务端”的真实身份......
基于CA证书的身份认证
数字证书概述
引用百度百科的定义:
数字证书是指在互联网通讯中标志通讯各方身份信息的一个数字认证,人们可以在网上用它来识别对方的身份。
数字证书(Digital Certificate)就好比现实世界中的身份证,用于标识一个网络用户(人、公司、服务器等)的合法身份。就像身份证必须由公安局来颁发,可信的数字证书也必须由一个权威机构来颁发,该机构就是证书授权中心(Certificate Authority,CA),CA 颁发的数字证书我们通常称作 CA 证书。
一个 CA 证书主要包含申请者的公钥、申请者的信息、签发机构 CA 的信息、有效时间、证书序列号等明文信息,同时也包含一个 CA 的数字签名,正是该签名的存在才证明了该证书的有效性。
数字签名建立在非对称加密算法的基础上,CA 在颁发证书时,会先将证书的明文信息用指定的算法(比如 SHA256 算法)计算出一个数字摘要,再使用 CA 的私钥对摘要进行加密,形成签名。
而证书的验证主要包含如下两部分:
- 检查证书的明文信息是否有效,比如证书是否过期、域名是否一致等。
- 用 CA 公布的公钥对证书签名进行解密,得到数字摘要1。再使用同样的算法对证书明文信息计算得出数字摘要2。对比数字摘要1和数字摘要2是否相等。
颁发证书的机构并非只有一个,比如机构 A 可以用 CA 颁发的根证书去给机构 B 颁发二级证书;机构 B 又可以用二级证书去给机构 C 颁发三级证书,以此类推,也即所谓的证书链。
使用CA证书认证通信双方的身份
现在,我们加入 CA 证书来认证通信双方的身份:
引入 CA 证书之后,服务端的公钥就放在它提供的证书之中,当客户端验证服务端证书通过后,也就说明其中的公钥确实是来自服务端的合法公钥。这样,后续的通信流程就可以正常地进行了。
然而,如果对称密钥一直不变的话,攻击者还是很有可能暴力破解出对称密钥。因此,我们还需要最好能够实现每次连接的对称密钥都是不同的......
使用随机数来生成对称密钥
为了使每次连接的对称密钥都不同,我们可以引入随机数来生成对称密钥,保证它的随机性。但是,考虑到当前计算机生成的随机数都是伪随机数,为了进一步提升随机性,我们可以通过生成多个随机数来达到此目的。
我们可以这么设计:
- 客户端和服务端先各生成一个随机数(随机数1和随机数2),在 ClientHello 和 ServerHello 报文中完成交换。
- 客户端在验证完服务端的身份后,再生成随机数3,通过服务端的公钥加密发给服务端。(此时,通信双方都拥有了 3 个一样随机数)。
- 客户端和服务端再根据这 3 个随机数,生成最终的对称密钥。
这样通过 3 个随机数来生成的密钥,就能较好地保证了密钥的随机性,降低被攻击者破解的可能。
虽然随机数1和随机数2是明文传输,但随机数3是密传输,也就能够保证攻击者很难破解到密钥。
到目前为止,我们已经通过多种手段成功阻止了攻击者窃听客户端和服务端之间的通信内容。但是,如果攻击者并不以窃听通信内容为目的,而是单纯地想搞破坏。比如,攻击者拦截了 ClientHello报文,把其中的随机数1改成了随机数4,这样就会导致客户端和服务端生成的密钥不一致。在此场景下,虽然连接已经建立起来了,但是客户端和服务端还是无法正常地通信:
为此,我们需要一种机制,校验连接建立阶段(握手阶段)所有消息的正确性,防止建立错误的连接......
校验握手消息的正确性
我们可以利用数字摘要来校验所有握手消息的正确性,也即,在握手阶段的最后,通信双方都通过 Hash 算法(比如 SHA256)对自己收到的和发送的所有消息计算出数字摘要,然后使用前面协商好的对称密钥对该数字摘要进行加密,发送给对方。
当收到对方发过来的数字摘要密文后,先用对称密钥对其进行解密,如果解密成功,说明密钥生成没问题;接着对比双方的数字摘要是否一致,如果一致,说明握手阶段的消息没有被篡改过,也即可以建立起正确的连接了。
到现在,我们基本上已经消除了窃听风险(通过数据加密)、冒充风险(通过证书认证)和篡改风险(通过数字摘要)。但是,为了建立起安全通信通道,我们需要经历多次消息交互、加解密、身份认证等步骤,对性能有一定的损耗。
因为经历过一次握手之后,密钥已经协商好,并且双方都保存了下来。下次连接建立时,完全可以沿用上次握手协商好的密钥,从而避免了重新协商密钥,提升了性能。我们需要一种重用会话的机制来提升协议的性能......
重用会话来提升性能
为了达到沿用上次协商好的密钥的目的,我们为每次连接都分配一个会话 ID。
- 在初次创建连接时,由服务端生成,并通过 ServerHello 返回给客户端。
- 在下一次创建连接时,客户端通过 ClientHello 把该会话 ID 发给服务端,表示希望重用该会话。
- 服务端在收到该会话 ID 后,就可以发送 Finished 消息,表示同意了会话重用,也即可以沿用上次会话协商好的密钥进行安全通信了。
到这里,我们已经完成了一个简易版的 SSL/TLS 协议的设计,真实的 SSL/TLS 协议当然没这么简单,但是里面的核心思想和基本原理都是类似的。只是 SSL/TLS 为了更好的安全性、扩展性和易用性等增加了一些机制,比如支持多种加密算法、使用 MAC 替代普通的数字摘要完成完整性校验。
下面,我们将简单介绍真实的 SSL/TLS 协议的工作机制。
SSL/TLS协议机制概述
SSL 协议(Secure Sockets Layer)是 TLS(Transport Layer Security)协议的前身,它们的版本演进如下,当前最新的版本为 TLS 1.3 版本。本节,我们将以当前使用最广泛的 TLS 1.2 版本作为分析对象,介绍 SSL/TLS 的基本工作机制。
SSL 1.0版本 -> SSL 2.0版本 -> SSL 3.0版本 -> TLS 1.0版本 -> TLS 1.1版本 -> TLS 1.2版本 -> TLS 1.3版本。
SSL/TLS协议总览
SSL/TLS 协议位于网络协议栈中传输层和应用层之间,它内部又可以分为 2 层,总共 5 种子协议:
Record协议
最底层的 Record 协议负责对上层子协议的封装,提供安全通信的能力:
- 私密连接。使用对称加密算法(比如 AES、RC4 等)来加密数据,而且在每次连接中,通信双方协商出来的加密密钥都是不同的,以此达到更好的安全性。另外,Record 协议也可以提供不加密的封装,比如在握手阶段的 Hello 报文。
- 可靠连接。使用 MAC(Message Authentication Code,消息验证码,TLS 目前使用的 HMAC 也属于 MAC 的一种)为数据提供完整性校验。同样,在握手阶段也可以不使用该功能。
Handshake 协议
上层的 Handshake 协议用在握手阶段,为通信双方提供身份认证、加密算法和密钥协商的能力:
- 身份认证。基于 CA 证书完成对端的身份认证,其中用到了非对称加密技术(比如 RSA、DES 等)。该功能是可选的,但通常的做法是至少进行单向认证。
- 安全参数的协商。完成用于安全参数的协商(比如加密算法、哈希算法、密钥等),并且能够保证在协商过程中,攻击者无法获取密钥。
- 可靠协商。确保在安全参数等协商过程中,攻击者无法对报文实施篡改。
Handshake 协议包含了如下几种报文类型:ClientHello、SeverHello、Certificate、ServerKeyExchange、CertificateRequest、ServerHelloDone、ClientKeyExchange、CertificateVerify、ChangeCipherSpec、Finished。
Change Cipher Spec协议
Change Cipher Spec 协议也用在握手阶段,当通信的一方发出Change Cipher Spec 报文时,就表示密钥已经协商好,从下一条消息开始,使用该密钥来进行加密传输。
Alert 协议
Alert 协议只有在连接异常时才会用上,当前协议定义的 Alert 消息类型如下:
close_notify: 表示发送方不会再发送任何消息,用于正常关闭连接,类似于TCP中的FIN报文
unexpected_message: 收到不在预期之内的消息
bad_record_mac: 收到的消息中MAC不正确,表示消息已经被篡改过
decryption_failed_RESERVED: 解密失败,用于TLS的早期版本
record_overflow: 消息长度溢出,密文长度不超过2^14+2048字节;压缩后的明文不超过2^14+1024字节
decompression_failure: 使用压缩功能时,解压失败
handshake_failure: 握手阶段无法协商出正确的安全参数
no_certificate_RESERVED: 为了兼容SSL 3.0版本,TLS不再使用
bad_certificate: 证书签名认证失败
unsupported_certificate: 收到不支持的证书类型
certificate_revoked: 收到被废弃的证书
certificate_expired: 收到过期的证书
certificate_unknown: 除上述4种情况外,其他证书异常场景
illegal_parameter: 握手阶段报文的参数非法,比如范围溢出等
unknown_ca: 不可信任的CA颁发的证书
access_denied: 证书校验通过,但发送方却拒绝继续握手
decode_error: 消息解码失败
decrypt_error: 握手阶段安全相关的步骤失败,比如签名校验失败、Finished消息校验失败等
export_restriction_RESERVED: 早期的TLS版本使用
protocol_version: 协议版本不支持
insufficient_security: 服务端要求的安全算法,客户端无法满足
internal_error: 协议内部错误
user_canceled: 用户非正常主动关闭连接
no_renegotiation: 拒绝重新握手
unsupported_extension: 不支持的扩展
Application Data 协议
Application Data 协议用在通信阶段,封装了应用层的数据,经由 Record 协议封装之后,通过 TCP 协议转发出去。
SSL/TLS 协议的握手过程
第一次握手
客户端向服务端发送 ClientHello 报文发起连接建立,其中携带了如下内容:
- Version: 客户端支持的TLS协议版本
- Random: 客户端生成的随机数,随后用于生成 master secret
- SessionID: 会话 ID,如果不为空,表示客户端想重用该会话
- CipherSuites: 客户端支持的加密套件列表,在 SessionID 为空时必须携带
- CompressionMethods: 客户端支持的压缩算法列表
- Extensions: 扩展内容
第二次握手
1.服务端向客户端发送 ServerHello 报文,其中携带了如下内容:
- Version: 选定的 TLS 版本,选择通信双方都支持的最高版本
- Random: 服务端生成的随机数,随后用于生成 master secret
- SessionID: 会话 ID,如果为空,表示服务端开启新的会话,并且不希望被重用;如果与客户端带过来的 SessionID 一样,表示重用该会话;否则,开启一个新的会话,并且在未来可能会被重用
- CipherSuite: 选定的加密套件
- CompressionMethod: 选定的压缩算法
- Extensions: 扩展内容
2.服务端向客户端发送 Certificate 报文,其中携带了服务端的证书,证书必须是 x.509 标准格式,包含服务端公钥、服务端域名、签发方信息、有效期等信息。
可选,客户端需要通过证书来认证服务端身份时发送。
3.服务端向客户端发送 Server Key Exchange 报文,其中携带了客户端用于生成 premaster secret 的安全参数。
可选,当 Certificate 报文中携带的信息无法支撑客户端生成 premaster secret 时发送。
4.服务端向客户端发送 CertificateRequest 报文,索求客户端证书,其中包含了期望的证书类型、签名算法和CA列表。
可选,开启双向认证时发送。
5.服务端向客户端发送 ServerHelloDone 报文,表示当前服务端已经把所有与密钥交换相关的内容都发送完毕。
第三次握手
1.客户端向服务端发送 Certificate 报文,其中携带了客户端证书。
可选,收到服务端的 CertificateRequest 报文时发送。
2.客户端向服务端发送 ClientKeyExchange 报文,其中包含了使用服务端公钥加密后的 premaster secret (随机数),随后用于生成master secret。
3.客户端向服务端发送 CertificateVerify 报文,其中包含了对通信双方到目前为止所有握手报文的数字签名,用于证明自己拥有的私钥与之前发送的证书中的公钥相对应。
可选,给服务端发送 Certificate 报文时发送。
4.客户端向服务端发送 ChangeCipherSpec 报文,表示从下条消息开始进行加密传输。
5.客户端向服务端发送 Finished 报文,加密传输,其中包含了所有握手消息的数字摘要,用于防篡改。
第四次握手
1.服务端向客户端发送 ChangeCipherSpec 报文,表示从下条消息开始进行加密传输。
2.服务端向客户端发送 Finished 报文,加密传输,其中包含了所有握手消息的数字摘要,用于防篡改。
最后
SSL/TLS 协议也并非绝对安全,它也有许多漏洞被黑客们不断地挖掘出来,当然,SSL/TLS 协议也在不断地完善。2018 年发布的 TLS 1.3 版本就在 TLS 1.2 版本的基础上做了许多增强。比如,在性能上,握手阶段从 2-RTT 缩减为 1-RTT,并支持 0-RTT 模式;在安全上,ServerHello 报文之后就开始加密传输、一些不安全的加密套件也不再支持(比如静态 RSA、Diffie-Hellman 等)。
虽然 TLS 1.3 版本机制上改变了很多,但是基本原理还是一样的。因此,把 SSL/TLS 协议原理理解透了,后续不管版本再怎么演进,我们都能快速完成协议机制的学习。
参考
The Transport Layer Security (TLS) Protocol Version 1.2,RFC 5246
The Transport Layer Security (TLS) Protocol Version 1.3,RFC 8446
SSL/TLS 协议运行机制的概述,阮一峰
Overview of SSL/TLS Encryption, MicroSoft Document
The First Few Milliseconds of an HTTPS Connection,Jeff Moser
为什么 HTTPS 需要 7 次握手以及 9 倍时延,面向信仰编程
HTTPS权威指南,杨洋、李振宇等
数字证书, 百度百科