学习HTTPS协议:对称加密、非对称加密、数字证书与TLS握手

学习HTTPS协议:对称加密、非对称加密、数字证书与TLS握手

引言:为什么需要 HTTPS?

HTTP 协议本身是明文传输,这意味着任何人都可以在传输过程中轻易截获和查看通信内容。例如,互联网服务提供商(ISP)或其他中间机构有可能在未经网站所有者批准的情况下,向网页中注入广告等内容。

HTTPS 正是为了解决这些安全问题而诞生的。它通过加密技术确保数据传输的机密性与完整性,一方面可以防止网页被恶意篡改,另一方面能在用户传输敏感数据(如登录银行账户、查阅电子邮件或访问医疗保险提供商)时,有效保障信息安全。

1
2
3
4
5
Without Encryption
This is a string of text that is completely readable

With Encryption
ITM0IRyiEhVpa6VnKyExMiEgNveroyWBPlgGyfkflYjDaaFf/Kn3bo3OfghBPDWo6AfSHlNtL8N7ITEwIXc1gU5X73xMsJormzzXlwOyrCs+9XCPk63Y+z0=

下面,我们将从基础的加密概念开始,逐步了解 HTTPS 是如何保护数据安全的。

加密技术基础

根据加密和解密是否使用同一密钥,加密技术可分为对称加密非对称加密

对称加密 (Symmetric Encryption)

对称加密是最为直观的加密方式,好比给保险箱配一把钥匙,开锁和上锁都用它。对称加密一般在加密速度、效率和资源消耗上表现较好,其主要问题在于密钥的分发和管理。

  • Alice 与 Bob 双方共享同一个密钥 $k$。
  • Alice 使用密钥 $k$ 加密消息 $x$,然后将加密后的结果 $k(x)$ 发送给 Bob。
  • Bob 使用相同的密钥 $k$ 解密消息,从而得到原始信息 $x$。

非对称加密 (Asymmetric Encryption)

非对称加密,也称公钥加密。网站服务器会生成一对在数学上相关联的密钥。使用公钥将明文加密后得到的密文,只能用与之相对应的私钥才能解密,公钥是公开的,而私钥必须保密。

  • Alice 与 Bob 事先互不相识,也没有可靠的通信渠道,但 Alice 需要通过不安全的互联网向 Bob 发送信息。
  • Alice 撰写好原文,此时的原文被称为明文 $x$。
  • Bob 使用生成一对密钥,其中一个作为公钥 $c$,另一个作为私钥 $d$。
  • Bob 可以通过任何方式将公钥 $c$ 传送给 Alice,即使窃听者 Charlie 在中途截获了 $c$ 也没关系。
  • Alice 使用公钥 $c$ 将明文 $x$ 进行加密,得到密文 $c(x)$。
  • Alice 同样可以通过任何方式将密文 $c(x)$ 传送给 Bob,即使 Charlie 截获了密文也无法解密。
  • Bob 收到密文后,用自己的私钥 $d$ 对其进行解密,即 $d(c(x))$,从而得到 Alice 撰写的明文 $x$。
  • 由于 Charlie 没有 Bob 的私钥 $d$,他无法解密密文,也就无法得知明文 $x$。
  • 如果 Alice 丢失了她自己撰写的原文 $x$,在没有得到 Bob 私钥 $d$ 的情况下,她将和 Charlie 一样,无法通过公钥 $c$ 和密文 $c(x)$ 恢复原文。
中间人攻击 (Man-in-the-Middle, MITM)

  1. 如果在 Bob 将公钥 $c$ 传给 Alice 的过程中,Charlie 将其替换成自己的公钥 $c_{charlie}$。
  2. Alice 之后便会用 Charlie 的公钥加密数据,传输的密文为 $c_{charlie}(x)$。Charlie 可以用自己的私钥 $d_{charlie}$ 解密 $d_{charlie}(c_{charlie}(x))$,从而窃取明文 $x$。

由于非对称加密的计算开销较大,通常会将其与对称加密结合使用:利用非对称加密安全地传递对称密钥,之后双方使用该对称密钥进行高效的数据加密和解密。除了非对称加密,密钥交换也可以通过其他方法完成,例如 Diffie–Hellman key exchange,它允许双方通过交换一些公开信息来生成一个只有他们自己才能计算出的共享密钥。

数字签名与证书

TL;DR

Q: 直接发公钥不安全
A: 引入证书颁发机构 (CA, Certificate Authority),客户端通过证书中拿到可信的公钥


Q: 证书可能被篡改呢?
A: 通过数字签名机制保证证书的完整性和可信性。

  • 证书经过 hash 运算(不可逆的加密算法)得到 digest, 使用 CA 的私钥对 digest 进行加密,加密结果即为数字签名
  • 客户端通过 CA 的公钥解密数字签名,得到明文 digest,同样对证书内容进行 hash 运算,比较两个 digest 是否一致

Q: 那CA 公钥怎么安全获取?
A: 操作系统/客户端内置 CA 的公钥列表,或通过浏览器/操作系统的更新获取。


Q: CA 机构很多,它们的公钥能都存储到客户端吗?
A: 可以通过证书链 (Certificate Chain) 的方式来获得信任,详细见下文[[#证书链验证流程]]

数字签名

在现实世界中,我们用身份证、签名、指纹等方式来证明身份。在数字世界里,数字签名扮演了类似的角色,其主要特点包括:

  1. 不可伪造 (Unforgeable):只有签名者能够生成签名。
  2. 身份认证 (Authentic):任何人都可以通过公钥验证签名的有效性。
  3. 不可重用 (Not Reusable):一个签名不能被用于其他消息。
  4. 不可篡改 (Unalterable):签名后的消息内容无法被篡改。
  5. 防止抵赖 (Non-repudiation):签名者不能否认其签名行为。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dsa

# 发送方类,负责签名消息
class Sender:
    def __init__(self):
        self.private_key = dsa.generate_private_key(key_size=2048)
        self.public_key = self.private_key.public_key()

    def sign_message(self, message: str) -> bytes:
        # 计算消息的哈希值
        hasher = hashes.Hash(hashes.SHA256())
        hasher.update(message.encode('utf-8'))
        digest = hasher.finalize()

        # 使用私钥生成数字签名
        signature = self.private_key.sign(digest, hashes.SHA256())
        return signature

    def get_public_key(self):
        # 返回公钥供接收方验证
        return self.public_key

# 接收方类,负责验证消息签名
class Receiver:
    def __init__(self, public_key):
        self.public_key = public_key

    def verify_signature(self, message: str, signature: bytes):
        # 计算消息的哈希值
        hasher = hashes.Hash(hashes.SHA256())
        hasher.update(message.encode('utf-8'))
        digest = hasher.finalize()

        try:
            # 使用发送方的公钥验证签名
            self.public_key.verify(signature, digest, hashes.SHA256())
            print("签名验证通过")
        except Exception as e:
            print(f"签名验证失败: {e}")

# 模拟消息发送与签名验证
sender = Sender()
receiver = Receiver(sender.get_public_key())

message = "我是奶龙(Official)"
# 发送方对消息进行签名
signature = sender.sign_message(message)

# 接收方验证签名
print("接收方开始验证签名...")
receiver.verify_signature(message, signature)

数字证书 (Digital Certificate)

证书颁发机构 (CA) 作为一个可信的第三方,核心职责就是验证网站身份并为其颁发数字证书,从而解决了公钥的可信分发问题。

证书申请与颁发流程

网站所有者需要生成一份证书签名请求 (CSR)。CSR 文件通常包含待签发证书的公钥、域名等识别信息,以及用于保证其真实性和完整性的数字签名。

CSR 首先被发送给注册机构 (RA) 进行初步审核。验证通过后,RA 会将 CSR 转发给 CA。CA 使用自己的私钥对 CSR 进行数字签名,最终生成并颁发数字证书给申请人。

数字签名流程

证书链验证流程

如前所述,接收方(如浏览器)需要用 CA 的公钥来验证证书的签名。自然想到,需要通过安全的机制将一批受信任的 CA 的公钥分发给客户端,实践中通常采用在操作系统/浏览器中预装等手段。

以 Windows 为例,其被安装在 Trusted Root Certification Authorities certificate store中,可参考Trusted Root Certification Authorities Certificate Store - Windows drivers | Microsoft Learn中的步骤访问这些证书

这样一来,经过Root CA(根证书授权机构)签署多个中级证书(Intermediate Certificates)便被认为是可以信任的,同理,由该中间机构签署的服务器证书也被认为是可信任的。

Danger

Wiki 中记录了一些Root CA 意外签署恶意证书的事故案例,都会让机构信誉受损。严重的可能被移除信任,不再内置到系统中。

以中国银行官网为例,可以看到存在三份证书,其中 www.boc.cn 的证书由 DigiCert CA1 签署

中国银行证书链
第一步:验证终端实体证书
  1. 接收证书:浏览器从中国银行服务器接收到 www.boc.cn 的证书。浏览器检查发现,该证书的签发者是 DigiCert ... CA-1
  2. 验证签名:为了确认该证书的真实性,浏览器需要找到 DigiCert ... CA-1 的证书,并提取其公钥。然后,用这个公钥去解密并验证 www.boc.cn 证书上的数字签名
第二步:验证中间证书
  1. 向上追溯:成功验证终端证书后,浏览器需要继续验证中间证书 DigiCert ... CA-1 本身的合法性。它发现该证书的签发者是 DigiCert Global Root CA
  2. 验证根证书:浏览器会在 Trusted Root Certification Authorities certificate store 中查找 DigiCert Global Root CA 对应的公钥,用公钥验证证书的真实性

通过这样逐级向上的验证,浏览器最终确认了 www.boc.cn 的身份是可信的。

SSL/TLS 握手

Note

SSL (Secure Sockets Layer) 是为 HTTP 开发的原始安全协议。后来,SSL 被功能更强、更安全的 TLS (Transport Layer Security) 所取代。因此,如今我们所说的 SSL 握手,实际上指的是 TLS 握手,但 “SSL” 这个名称因历史原因仍被广泛使用。

从技术上讲,HTTPS 并非一个独立于 HTTP 的新协议,它本质上是“HTTP over TLS/SSL”,即在 HTTP 协议的基础上,通过 TLS/SSL 进行加密传输。

TLS握手流程

TLS 握手发生在 TCP 三次握手之后,其核心目标是:

  • 协商版本:确定双方将使用的 TLS 版本(如 TLS 1.2, 1.3)。
  • 协商加密套件:确定将使用的加密算法组合 (Cipher Suites)。
  • 服务器身份验证:客户端通过验证服务器的数字证书来确认其身份。
  • 生成会话密钥:安全地生成一个对称密钥(会话密钥),用于后续通信的高效加密。

TLS 握手的具体步骤会因所选的密钥交换算法和密码套件而异。若想深入了解,可参阅 What happens in a TLS handshake? | Cloudflare

参考资料

学习HTTPS协议:对称加密、非对称加密、数字证书与TLS握手

https://vluv.space/HTTPS协议/

作者

GnixAij

发布于

2025-07-26

更新于

2025-07-26

许可协议

评论