简单记录一下近日学习HTTPS、TLS等协议的收获和心得。
1. HTTPS定义
超文本传输安全协议(英语:HyperText Transfer Protocol Secure,缩写:HTTPS;常称为HTTP over TLS、HTTP over SSL或HTTP Secure)是一种通过计算机网络进行安全通信的传输协议。HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网站服务器的身份认证,保护交换资料的隐私与完整性。
2. HTTPS与HTTP的区别
-
HTTP的URL是由“http://”起始与默认使用端口80,而HTTPS的URL则是由“https://”起始与默认使用端口443。
-
HTTP基于明文传输,不安全,攻击者可以通过监听和中间人攻击等手段,获取网站帐户和敏感信息等。HTTPS的设计可以防止前述攻击,在正确配置时是安全的。
3. 加密算法
在介绍HTTPS之前,首先来说一下加密算法。加密算法有双向和单向之分,双向加(解)密算法指明文和密文可相互转化,单向加密算法则只能由明文转化为密文,无法逆向转化。下图是两种分类所主要用到的算法。
![](https://i-blog.csdnimg.cn/blog_migrate/29d301c7e24c308263492e1e162a7618.png)
其中来着重介绍对称加密和非对称加密,以及二者的结合。
3.1 对称加密
可以用一组公式来总结:
f1(k, data) = encrypted_data
f2(k, encrypted_data) = data
其中f1
,f2
分别为加密,解密算法。k
为密钥,data
为明文数据,encrypted_data
为加密数据。
流程:客户端和服务器共同协商出一个密钥和一个对称加密算法,后面传输数据时利用密钥和对称加密算法对数据进行加密,另一端收到后,利用解密算法得到原始明文数据。
3.2 非对称加密
非对称加密有公钥(public_key)和私钥(private_key),二者一一对应,在生成私钥的同时也生成对应公钥。
公式总结如下:
f(pub_key, data1) = encrypted_data1
f(pri_key, encrypted_data1) = data1
-------------------------------------
f(pri_key, data2) = encrypted_data2
f(pub_key, encrypted_data2) = data2
其中pub_key
,pri_key
分别为公钥和私钥。通过公钥和私钥都可以给数据加密,且使用对应的私钥/公钥可对数据解密。
流程:客户端向服务器发出请求索要公钥,服务器返回公钥。客户端将数据通过公钥加密后发给服务器,服务器通过私钥进行解密;服务器通过私钥对数据加密后发给客户端,客户端利用公钥解密。
3.3 对称加密 + 非对称加密
非对称加密的效率,对称加密的安全性是其二者的缺点,所以可以将二者结合,实现取长补短。
流程:客户端向服务器发出请求索要公钥,服务器返回公钥。客户端随机生成一个随机数num,num通过公钥加密后发送给服务器,服务器利用私钥解密,得到num。这时,客户端和服务器将num共同视为对称加密的密钥,后面传输的数据通过此密钥加/解密。
4. 数字证书和数字签名
在接收到数据包时,会出现一个问题:如何确定此数据包是不是真正目的服务器所发出的(中间人攻击会冒充服务器,向客户端发送数据包、公钥)。这时就需要「数字签名」和「数字证书」技术来验证文件内容的一致性与服务器的身份(安全性更高的场景还需要验者客户端的身份)。数字签名算法主要有RSA(哈希算法 + RSA加密算法),DSA,ECDSA。
-
小明首先生成公钥和私钥,将文件内容进行哈希运算得到文件摘要(文件哈希值),再利用私钥和RSA加密算法对摘要加密,得到数字签名。
-
小明将文件、公钥、数字签名发送至小红,小红接收到网络包,利用拿到的公钥对数字签名解密,得到文件摘要1(文件哈希值);对文件进行哈希运算,得到文件摘要2。摘要1和摘要2相同则签名成立,反之不成立。摘要1和摘要2相比较这一步可确保文件在传输过程中不被篡改(若文件被篡改,文件哈希运算得出的摘要2与解密签名得到的摘要1不同)。
-
又引出了一个问题:小红如何确保得到的公钥一定是小明生成的?
小明将其公钥和部分身份信息发送给CA(Certificate Authority),CA在核实小明身份后,颁发数字证书(确保公钥和小明的一一对应关系),小明只需要将数字证书一起发送给小红,小红即可确定收到的公钥是否为小明生成。
-
如何确保数字证书不被篡改?
CA有自己的公钥和私钥,通过私钥对小明的文件、公钥进行哈希摘要 + RSA加密,得到CA的数字签名,放在小明的数字证书中,而CA根证书为了不被伪造,被嵌在客户端和服务端的操作系统或浏览器中。
在实际环境中,服务器发来的证书通常会有多个证书组成证书链。例如:根 CA 证书 -> 中级 CA 证书 -> 服务端证书。客户端在验证服务端证书的有效性有这样的一个过程。首先会找到该证书的认证证书,也就是中级 CA 证书。然后找中级 CA 证书的认证证书,可以是另一个中级 CA 证书,也可能是根 CA 证书,这样直到根 CA 证书。接着从根 CA 证书开始往下去验证数字签名。用 CA 证书的公钥去验证中级证书的数字签名,再用中级证书的公钥去验证服务器证书的数字签名。任何一个环节验证失败,就可以认为证书不合法。
5. TLS握手
TCP 三次握手结束和服务器成功连接后,客户端就开始发起TLS连接,首先会进入**TLS握手阶段**。
TLS握手阶段目的:
-
协商加密密钥
用来对后面的 HTTP 协议等应用协议内容进行加密。这个密钥又称为主密钥,为加密算法的密钥。
-
协商加密算法
为了能够提供效率,使用对称密钥。对称加密使用的是位运算,速度快,甚至可以硬件加速。非对称加密比如 RSA,使用了大数乘法等,整体会比较慢。对称加密只要密钥没有泄漏,那也是非常安全的。这也是后面 SSL 握手协议要确保的。
-
验证身份及数据完整性
通常情况下,只要验证服务端身份。特殊情况下,比如一些安全级别高的应用场景,还要验证客户端身份。服务端会返回证书链,有根 CA 证书在里头。通过证书的链式担保,可以确认服务端是否是可信任的。同时,在握手期间,公钥传输成功后,还会对某些信息进行数字签名,确保数据没有被篡改且身份无误。
TLS握手阶段一般流程(TLS握手的流程并不是一成不变的,根据实际的应用场景来,主要有三种):
-
只验证服务端
这个用三个阶段就完成握手,此次Wireshark的请求也是这样。一般的网络请求也仅仅到这个程度。
-
验证服务端和客户端
在安全性要求较高的场景,服务端也要验证客户端的身份。方式也是发证书证明自己。
-
恢复原有会话
这个属于HTTPS 优化的范畴。使用 Session Ticket 或者 Session ID 机制恢复之前已经完成握手的会话。这个是可以允许在不同的 TCP 上进行的。因为握手的加密数据已经保存,直接恢复就可以开始传递了。Session Ticket 由客户端保存加密信息,Session ID 的方式由服务端保存加密信息。不过 Session Ticket 在 Android 客户端还没有得到广泛的支持,和具体机型和内置的 OpenSSL 的版本有关。
TLS握手机制图解
Client Hello
在Wireshark软件中捕获了访问知乎网页数据包,追踪TCP流得到第一个TLS包
![](https://i-blog.csdnimg.cn/blog_migrate/61e984fa06bcb941b4150277e32cb540.png)
-
类型
-
长度
-
版本
TLS 1.2 也是 SSLv3.2。这是 SSL 客户端能够支持的 SSL 最高版本
-
随机数
生成一个32字节随机数(图中的随机数1)。最后加密数据用的主密钥,需要客户端和服务端一起协商出来。后面服务端的 Server Hello 阶段也会生成一个随机数。一同用来计算出主密钥。
-
会话ID
这个 Session ID 是可以重用的,具体看服务端资源和支持情况。如果要复用 Session ID, SSL 服务端需要维护连接的状态和上次握手成功留下的加密信息。如果是第一次访问该网址,会话 ID 尚未创建,客户端没记录,为 0。如果客户端保存了 Session ID 的信息,下次发起 SSL 请求的时候会带上。
-
加密套件
客户端可以支持的密码套件列表。这些套件会根据优先级排序。每一个套件代表一个密钥规格。以 “TLS” 开头,接着是密钥交换算法,然后用 “WITH” 连接加密算法和认证算法。一个加密套件有这么几个内容:密钥交换算法、加密算法(会带有支持的最高密钥位数)、认证算法还有加密方式。最终使用什么密码套件是服务端决定的。要什么密码套件会在 Server Hello 中进行反馈。
-
压缩算法
这里为 0,说明不支持压缩算法
-
扩展字段
一些扩展信息,比如 SNI 的支持,ALPN 的信息等等
Server Hello
![](https://i-blog.csdnimg.cn/blog_migrate/fcddf4093bd5b9003624a5b333a10ed7.png)
-
类型
-
版本
指定这次 SSL 使用 TLSv1.2 版本
-
随机数
上面的 Client Hello 过程也生产了一个 32 位随机数,这两个随机数将参与主密钥(master key)的创建。(对应图解中随机数2)
-
会话ID
-
加密套件
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 。这个是从客户端 Client Hello 上传的加密套件中选中的,根据密码套件的格式,上面的信息有,
- 交换加密算法为ECDHE ,就是EC Diffie-Hellman ,RSA 表示后面 Server Key Exchange 阶段的携带 DH 加密算法的公钥的包的数字签名的加密算法是 RSA;
- 加密算法为 AES ,最高密钥支持 128 位
- 认证算法 SHA256
-
压缩方法
这里为 0,表示不使用压缩算法
Certificate
服务端下发证书,客户端验证服务端的身份,并且取出证书携带的公钥,这个公钥是交换加密算法的公钥。也就是在 Server Hello 阶段指定的 ECDHE (EC Diffie-Hellman)算法,也是通常说的 DH 加密。
这个 Certificate 消息下发了从携带自己公钥的数字证书和 CA 证书的证书链,在 Certificates 字段中:
由字段信息可知,证书链中共有3个数字证书。分别为服务端(zhihu.com)证书 -> 中间CA证书 -> 根CA证书。首先看服务端证书
![](https://i-blog.csdnimg.cn/blog_migrate/b0138b538e67115984ad9e3b7298a95e.png)
首先是 signedCertificate 字段的内容,即数字证书的数据
-
版本
对应的就是 X.509 V3 标准
-
序列号
serialNumber,证书颁发者唯一序列号。
-
签名算法ID
这里指的是使用 SHA-256 进行摘要,RSA 进行加密的签名算法。
-
证书颁发者
issuer,就是颁发该证书的 CA 的信息。里面携带后该 CA 的唯一名称(DN,Distinguished Name),比如国家为 US(美国),组织机构为 DigiCert Inc.,名称为 GeoTrust CN RSA CA G1。后面我们需要从证书链找到该 CA 证书(具体方法:在其他证书的subject字段查找国家、组织机构、名称),去认证当前证书
-
有效期
validity,证书的起始时间和终止时间
-
对象名称
subject,里面就是该证书的名称等主要信息了。比如国家为 CN(中国),组织为智者四海(北京)技术有限公司。名称为 *.zhihu.com ,也是该证书适用的域名。
-
对象公钥信息
subjectPublicKeyInfo。因为这是服务端证书,这个公钥后面将用于主密钥的交换过程,从中可以了解到这个公钥采用 RSA 加密
-
扩展部分
一些扩展信息。比如对象的别名。这个如果是 CDN 的服务器证书,那么别名将会非常多。
然后是证书颁发机构的签名信息:
-
签名算法
algorithmIdentifier,这里得出使用的还是 SHA-256 摘要加 RSA 加密的签名算法。这个就是认证该证书的 CA 证书使用的签名算法。
-
签名信息
encrypted,这个信息的内容,CA 证书对 SHA-256 对上面的数据部分进行摘要后,使用 RSA 的私钥加密获得。后面会用在该证书的认证过程,取出 CA 证书的公钥,解密签名信息,用同样的算法获取数据摘要,对比一下是否相同。
Server Key Exchange
![](https://i-blog.csdnimg.cn/blog_migrate/9d64c9731fadde5d307d0bb923954990.png)
密钥交换阶段,这个步骤是可选步骤,对 Certificate 阶段的补充,只有在这几个场景存在:
- 协商采用了 RSA 加密,但是服务端证书没有提供 RSA 公钥。
- 协商采用了 DH(EC Diffie-Hellman) 加密,但是服务端证书没有提供 DH 参数。
- 协商采用 fortezza_kea 加密,但是服务端证书没有提供参数。
Server Hello Done
![](https://i-blog.csdnimg.cn/blog_migrate/acf1fb0b4ea0dc6de52d14a795693089.png)
通知客户端,版本和加密套件协商结束。
通过 Wireshark 抓包发现了一个现象,就是 Server Key Exchange 和 Server Hello Done 被放到了同一个 SSL 记录协议中,这是因为 SSL 记录协议具有组合功能。客户端收到这样的包后,会处理成两个单独的协议包,这又是 SSL 记录协议的分组功能。
Client Key Exchange
![](https://i-blog.csdnimg.cn/blog_migrate/fb90bc083e1c67aa25df394224dd55f0.png)
这里,客户端不直接生成加密密钥,而是通过之前客户端和服务端生成的随机数又再生成一个随机数,使用前面协商好的用 EC Diffie-Hellman 算法进行加密传输给服务端。这个值又被称为 “premaster secret“。
服务端收到这个报文后,会使用自己的私钥解开这个随机数。在这个阶段过后,服务端和客户端都有三个随机数:客户端随机数、服务端随机数和预备主密钥。在服务端收到了 Client Key Exchange 消息后,两端都按照相应的算法生成了主密钥,加密密钥交换完成。交换完了,因为主密钥是两个端按照约定好的算法产生的,如何保证这个主密钥是正确的?这时候会进入下一个阶段。客户端和服务端会对握手信息使用 SHA 做个摘要,用 AES 加密算法和主密钥加密,传递给对方验证。这种方式也称为消息认证。就是下面的过程:
Change Cipher Spec (Client)
![](https://i-blog.csdnimg.cn/blog_migrate/11fa64cdf6a467682706ee57976db845.png)
客户端通知服务端,后续的报文将会被加密。
Encrypted Handshake Message (Client)
![](https://i-blog.csdnimg.cn/blog_migrate/0057a40ead80141dd365b964df744cd5.png)
这里就是客户端的 Client Finished 消息。也是整个 SSL 过程中,发送给服务端的第一个加密消息。服务端接收后,服务端用同样的方式计算出已交互的握手消息的摘要,与用主密钥解密后的消息进行对比,一致的话,说明两端生成的主密钥一致,完成了密钥交换。
Change Cipher Spec (Server)
![](https://i-blog.csdnimg.cn/blog_migrate/9f5c35f421e4b5fca9fef8628ff80fc9.png)
服务端通知客户端,后续的报文将会被加密。
Encrypted Handshake Message (Server)
![](https://i-blog.csdnimg.cn/blog_migrate/58d3c62bb34cd2f204d4539c35fe3a57.png)
这里就是服务端的 Server Finish 消息。和上面的客户端的 Encrypted Handshake Message 一样,是服务端发出的第一条加密信息。客户端按照协商好的主密钥解密并验证正确后,SSL 握手阶段完成。
Application Data
![](https://i-blog.csdnimg.cn/blog_migrate/4dfaf1d6fd33f20213c8d4aaa5667fad.png)
应用数据传输消息。因为这里是 HTTPS,所以可以对 HTTP 应用协议数据加密然后传输了。