最初的 HTTP 以明文传输方式运行在 Internet 上。随着越来越多安全敏感型应用的出现,HTTP over TCP 无法提供足够的安全性保障。SSL 及其继任者 TLS 在这样的背景下产生,提供面向信道的安全性。本文着力于展现 HTTP over TLS (即 HTTPS)的工作方式,以及与此密切相关的数字证书、对称/非对称加密等,但不涉及密码学相关实现及 SSL/TLS 的安全性证明。

概述

传输层安全性协议(Transport Layer Security, TLS) 及其先行者安全套接层(Secure Sockets Layer, SSL) 都是一种安全协议,旨在保障通信安全:

  • 防窃听:避免第三方获知通信双方的通信内容
  • 防篡改:避免第三方修改通信双方交换的通信内容
  • 防伪造:避免第三方伪造身份参与通信

从 TCP/IP 协议栈上看,SSL/TLS 工作在应用层,作为基础性的安全协议向上服务其它应用层协议(FTP, HTTP, SSH 等)。普遍的,应用层协议使用两个不同的常用端口来区分服务是否建立在 SSL/TLS 之上。比如 HTTP 协议的 80, 443、FTP 协议的 21, 990。

TCP/IP Stack

SSL/TLS 运行过程

SSL/TLS 的核心是为上层协议(本文特指HTTP协议)提供安全性保障,即对信息进行加密。

加密又分两类:对称加密与非对称加密。在对称加密中,通信双方使用相同的密钥进行加解密操作,这种技术能够提供比较高的运算效率,但双方必须提前约定密钥;而非对称加密恰恰能够解决密钥约定问题,它通过通信一方 A 向另一方 B 公开提供公钥来允许 B 进行加密,由 A 使用保留的配对私钥完成解密,但这种方案带来的是运行效率的极大下降。

建立在 SSL/TLS 上的应用信息使用对称加密来完成信息交互。在能够保证密钥泄露的提前下,通信的安全性是能够得到保证的。

密钥约定

通信双方如何对密钥进行约定?在开始传递 HTTP 数据前,SSL/TLS 需要进行握手,对通信涉及到的必需信息进行协商:

  1. 对称加密使用的算法
  2. 对称加密使用的密钥
  3. 通信使用的压缩算法

其中对称加密算法和压缩算法的协商比较容易实现,毕竟算法实现都是公开的。难点在于如何保证在不安全的网络环境下协商密钥。

握手过程采用非对称加密来确保通信双方对密钥的协商。涉及几个阶段:

Client Hello 阶段

TCP 三次握手完成后,Client 向 Server 发起 Client Hello 消息。消息中携带随机数(Client Random)、会话ID、客户端支持的加密方案列表、客户端支持的压缩算法列表。

  • 随机数由四字节的时间戳、28 字节的随机数共同组成。

  • 加密方案列表的每项方案均是密钥交换算法、批量加密算法、摘要算法的组合。密钥交换使用非对称加密算法实施,提供的选项包括 RSA、Diffie-Hellman 算法(包括临时公钥的变种)。

Server Hello 阶段

Server 收到 Client 端发起的 Hello 消息后,依据客户端提供的加密、压缩可选项,回复选用的加密方案、压缩算法以及随机数(Server Random)和会话ID

  • 会话ID: Server 端收到 Client 的会话ID 后,将优先从服务器的会话缓存中查找匹配项;如果发现匹配,服务器一般会恢复原有会话,此时回复的会话ID必须与 Client 发送的会话ID一致。如果没有匹配,或服务端不同意恢复会话,则响应一个不同的会话ID,或返回为空。

Server Certificate 阶段

Server Certificate 消息始终紧跟着 Server Hello 消息一起响应。一般提供 X.509 证书链,Client 借此来校验 Server 是否合法。

Server Key Exchange 阶段

一般来说,如果选择迪菲赫尔曼算法作为密钥交换算法,Server 产生一个随机数 y 和另外两个基础数 p, g,将(p, g, PubKey_A = g^y mod p) 发送给 Client。

Server Hello Done 阶段

该阶段是 Server Hello 结束的标志,不携带消息内容。

Client Key Exchange 阶段

Client 针对收到的 (p, g, PubKey_A),同样生成一个随机数 x,将 (PubKey_B = g^x mod p) 发送给 Server。

Client 和 Server 将根据各自拥有的信息 (p, g, x, PubKey_A) 和 (p, g, y, PubKey_B),计算出相同结果的 premaster-secret

$$ premaster_secret=PubKey_A^x mod p premaster_secret=pubkey_B^y mod p $$

再结合握手阶段各自交换的 Server Random 和 Client Random ,计算得到对称加密所需的密钥。

$$ master_secret = PRF(premaster_secret, “master secret”, client_random + server_random, 48) $$

X.509 证书

依赖于 SSL/TLS ,HTTPS 通信双方借助非对称加密完成密钥协商,依赖密钥实现对通信信息的加密传输。但是,Client 如何判断 Server 的身份?Server 如果想要确认 Client 的身份?这就需要依赖于证书(Certificate)了。

在开始前,罗列一下专有名词:

  • PKI (Public Key Infrastructure): 公开密钥基础设施。很大程度上安全通信保障机制就是基于 PKI 建立的。
  • end entity: PKI 证书的用户或用户系统
  • CA (Certification Authority): 证书授权机构
  • RA (Registration Authority): 证书注册机构
  • CRL issuer: 生成并签名 CRL 的系统
  • repository: 存储证书和 CRL 的单个系统或分布式系统,同时用于分发证书和 CRLs 到 end entities

证书是整个 PKI 的核心,是网络上的身份证明文件。在通信过程中,通过相互确认身份证明文件,才能确认自己在和对的人通信。但是,仅仅只是身份证明文件,并不能拿来做为证明。毕竟,也存在伪造的可能。这就需要权威机构来发行身份证明文件。CA 就在网络上证书发行的权威机构,像我们熟知的 “Let’s Encrypt”, “DigiCert” 等都是 CA 机构。

另一个问题,CA 机构如果在网络上证明它的身份?证书需要借助证书发行机构证明文件真伪,这形成了一条证书链,链的根证书,就是常称的 CA 证书。为避免无限递归的结果,CA 证书采用自证的方式,证书主体及签署者都是自己。而在真实世界中,由 WebTrust 机构负责对 CA 机构进行审计,而浏览器厂商只会默认认可经过 WebTrust 机构审计过的 CA 机构的证书。这也就避免了自签名证书在网络上泛滥的问题,毕竟,发行一个自签名的 CA 证书在技术上没有难度。

证书结构

一张常见的证书包含下列数据:

  • 版本(Version):识别用于该证书的 X.509 标准的版本,目前有v1,v2,v3
  • 序列号(Serial Number):CA 机构对其发行的证书赋予的唯一识别编号。在未来需要撤销证书时,将序列号添加到 CRL 发布到网络上。
  • 证书签名算法(Signature Algorithm)
  • 签署者(Issuer):发行证书的机构,一般是根 CA 证书。为这张证书做背书。
  • 有效期(Validity):每个证书只能在一段有限的时期内有效。规定了起止日期。
  • 主体(Subject):服务提供者在网络上的可识别名称。
  • 主体公钥信息(Subject Public Key Info):提供主体的公钥,用于 SSL/TLS 认证。与证书一起被签署,被证书链信任。
  • 证书签名(Signature)

下列是一张 CA 自签名证书。

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 14954122101612897031 (0xcf87b6d232746307)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=ZheJiang, L=HangZhou, O=ffutop.com, OU=www.ffutop.com, CN=www.ffutop.com/[email protected]
        Validity
            Not Before: Dec 14 00:43:06 2019 GMT
            Not After : Sep 12 00:43:06 2029 GMT
        Subject: C=CN, ST=ZheJiang, L=HangZhou, O=ffutop.com, OU=www.ffutop.com, CN=www.ffutop.com/[email protected]
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:a1:89:3f:fe:a5:bf:95:40:11:e6:20:21:78:81:
                    52:30:e9:cb:c8:bd:01:18:25:1d:0a:88:a6:8e:f5:
                    f2:7a:38:e3:b1:8e:96:ac:2c:d2:bd:f0:9b:6a:2c:
                    7a:d6:cf:71:6d:17:bf:15:cd:e8:e8:27:6a:e7:18:
                    48:dd:32:b7:9a:5f:2c:15:b8:69:99:6e:73:3e:9a:
                    cb:9f:77:fe:3e:7a:f1:c6:eb:0d:85:5c:9e:44:2d:
                    a1:e0:9b:30:72:14:c5:e0:4f:9e:fe:c4:3c:ce:6d:
                    95:c7:3d:ff:77:90:f5:c2:00:09:1e:6e:0d:28:f2:
                    51:bf:b3:00:d8:e7:02:50:80:76:cc:25:aa:38:14:
                    c3:15:b5:0b:11:34:20:92:27:8a:ac:2f:de:47:f6:
                    7c:f3:04:5c:e5:c1:d3:f7:88:03:92:bd:f2:47:2b:
                    83:c0:3c:d7:5b:6c:4a:dd:4d:ff:e3:50:9c:23:29:
                    75:8b:f6:fe:d9:1b:d4:c7:fc:81:70:b2:bf:5c:10:
                    62:64:8b:4a:d6:33:df:f7:fd:00:ea:4f:17:cb:4d:
                    d3:25:fb:61:68:14:0d:1d:86:b7:5f:0d:13:40:6b:
                    ad:27:21:bc:90:f4:eb:e6:84:c8:bf:dc:aa:89:43:
                    18:64:2a:b1:78:61:30:82:2d:fa:e1:82:38:f7:4b:
                    b8:a9
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         81:5e:de:aa:89:e6:15:97:69:ae:14:99:56:94:7d:57:4e:09:
         0e:0c:b4:93:86:5a:4d:c2:e3:4e:86:ea:7e:9b:07:2e:88:16:
         60:75:44:ba:4f:52:ae:e9:c1:bb:d9:13:4e:8f:47:28:7d:43:
         e9:3c:40:dd:ed:d1:15:ad:e0:c4:6f:bd:f7:13:f3:b6:4c:39:
         b5:f8:1e:f1:cd:b5:82:92:da:59:39:f5:0b:ce:3f:e6:a7:84:
         2a:af:b6:88:9d:18:f3:0b:e3:cf:13:a7:9f:c6:18:5f:d7:66:
         f5:e1:39:01:7f:06:c3:16:a2:61:aa:20:03:8f:30:15:99:a3:
         c5:5c:f7:8d:d6:a9:3b:76:c0:16:e8:ac:5e:9b:40:fe:84:23:
         f2:75:e7:54:2e:fb:e7:0a:c2:86:79:a9:f0:a6:db:37:ac:2d:
         2e:21:53:98:3b:12:4f:91:8b:5e:5d:84:13:95:c0:f3:ef:bb:
         05:e7:06:c2:99:9a:ec:eb:e2:9c:33:b9:23:fa:1d:3f:18:66:
         3a:70:bd:ff:92:7a:d7:31:be:e1:45:dc:49:0c:c7:39:a7:dd:
         3d:b1:a9:bc:0d:4a:48:5a:f1:40:97:3e:fe:96:fa:18:71:39:
         2f:f2:6c:c4:03:82:b3:90:02:58:14:a8:4e:3a:f5:99:97:2f:
         5d:2e:2d:92

参考

[1]. RFC 2246