通信中的加密与解密
通信中的加密与解密
你每天都在用 HTTPS,但你知道浏览器和服务器之间究竟发生了什么吗?这篇文章从对称加密讲起,一路走到证书体系和 TLS 握手,用生活化的例子和可运行的 Rust 代码,把「通信中的加密与解密」彻底讲清楚。
为什么需要加密
通讯双方为了保证消息不被别人窃取,就需要加密。
比如小明需要输入银行卡号 6222 1234 5678 9012。如果直接发送,黑客小黑在网络上拦截到了,就能看到卡号并盗刷。这就需要加密。浏览器把卡号变成乱码 xK9#mP2@qL… 发送出去,即使小黑拦截了也看不懂。商家用密钥解密后才能看到真实卡号。
但是我们怎么能把 6222 1234 5678 9012 换成 xK9#mP2@qL… 发送出去以后,别人还能正确解析呢?这就涉及到了我们需要在通讯之前就指定一些加密的方法。
加密方法可以通过加密算法实现。
加密算法一般分为对称加密和非对称加密。如果密钥相同就是对称加密,反之就是非对称加密。
对称加密算法
背景
在古代,双方间谍需要交换信息,往往会提前设定一个规则,比如某个字映射为另一种表达,或者等等其他规则。
假设一种情况,以银行卡 6222 1234 5678 9012 为例,我可以与银行方协商。我们可以共同把所有数字加一或者减一来发送,到时候你接受到我消息的时候,需要对应进行加一或者减一来获取到真实的信息。那么我在发送卡号之前,我就会对卡号进行处理。
- 加一:6222 1234 5678 9012 -> 7333 2345 6789 0123
- 减一:6222 1234 5678 9012 -> 5111 0123 4567 8901(假如 0-1 以后是 9)
这样的话传输过程中别人如果不知道你们定的规则就会获得一个错误的卡号,也就不会对你产生什么伤害了。
通用的对称加密算法实现方法
DES 算法、3DES 算法、AES 算法。以下三种算法了解思路即可,实际开发直接调用成熟库,无需自行实现,如果有兴趣可以深入研究。
- DES (Data Encryption Standard):使用 64 位分组、56 位密钥 的 Feistel 网络,通过 16 轮 的置换函数(包括扩展置换、子密钥异或、S 盒代换、P 盒置换)实现加密与解密的对称性。
- 3DES (Triple DES)采用 EDE (Encrypt-Decrypt-Encrypt) 结构,对 64 位块 进行 三次 DES 运算,使用 112 位或 168 位 的密钥长度(两组或三组 56 位子密钥),显著提升了安全性。
- AES (Advanced Encryption Standard)基于 Rijndael 算法,在 128 位块 上执行 10/12/14 轮(取决于密钥长度为 128/192/256 位)的操作,每轮包含 SubBytes、ShiftRows、MixColumns 和 AddRoundKey 四个核心步骤。
Rust 实现 AES-128-CBC 对称加密 demo
1 | |
1 | |
看起来对称加密很完美,但有一个致命的问题:密钥怎么安全地传给对方?
回到刚才的例子。小明和商家约定”所有数字加一”,但问题来了:小明和商家是第一次打交道,他们怎么约定这个规则呢?
如果小明直接在网上发消息说”咱们用加一的规则吧”,小黑也能看到这条消息,那加密就白费了。如果提前线下见面约定规则,那网购还有什么意义?总不能每次买东西都先飞去商家总部一趟吧。
这就像你想给陌生人寄一个上锁的箱子,但你们俩必须用同一把钥匙。问题是:钥匙本身怎么安全地送到对方手里?
更现实的场景,想象一下:
- 京东有几亿用户,难道要和每个用户都约定一个不同的加密规则吗?
- 你今天在淘宝买东西,明天在拼多多买东西,难道要记住几十个不同网站的加密规则?
对称加密的核心问题:通讯双方必须事先共享同一个密钥,但这个密钥本身的传递就是个安全难题。
非对称加密算法
背景
非对称加密算法使用两个密钥来处理加密与解密。我只需要保存好我解密的私钥,至于公钥我完全可以公开。你可以把非对称加密想象成一个信箱。
公钥(公开的锁) 和 私钥(唯一的钥匙)。
- 公钥(锁):我把这把锁挂在门口,告诉全世界:”谁想给我送秘密信件,就用这把锁把箱子锁上。”
- 加密(锁箱子):你拿到这把锁(公钥),用它把信锁进箱子里。
- 私钥(唯一的钥匙):箱子上锁之后,只有我手里的这把唯一的钥匙(私钥) 能打开它。
为什么公钥可以暴露?
因为方向是单向的。
- 公钥只负责”锁门”(加密),它无法执行”开门”(解密)。
- 哪怕全世界都有这把锁(公钥),他们也只能把信息”塞进去”,却无法把已经锁在里面的信息”取出来”。
- 只有掌握私钥的我,才能解密。
为什么公钥无法解锁呢?
先从一道数学题说起,请你做这道题:
17 * 19 = ?
你很容易就能算出来是 323。
现在反过来:
323 是哪两个质数相乘得到的?
是不是稍微要想一会儿?
如果把数字换成一个 600 位的大数,正向乘法计算机眨眼就算完,但反向「拆解」即使是全球最快的超级计算机,算到宇宙灭亡也算不出来。
这就是 RSA 加密的全部秘密——它把安全性建立在这个「乘法容易、分解极难」的数学事实上。
通用的非对称加密算法实现方法
RSA 算法、ECC 算法、ElGamal 算法。以下三种算法了解思路即可,实际开发直接调用成熟库,无需自行实现,如果有兴趣可以深入研究。
- RSA:基于大整数质因数分解难题,密钥长度常用 2048 位或 4096 位,是目前最广泛使用的非对称加密算法。
- ECC(Elliptic Curve Cryptography):基于椭圆曲线离散对数难题,同等安全强度下密钥比 RSA 短得多(256 位对应 RSA-3072 位),计算速度更快,常用于移动端和 HTTPS。
- ElGamal:基于离散对数难题,是 GPG(邮件加密)等工具的理论基础,使用相对较少。
Rust 实现 RSA-OAEP 非对称加密 demo
1 | |
1 | |
看起来非对称加密很完美解决了密钥传递的难题:公钥随便公开,私钥只有自己持有,再也不用提前约定密钥了。
但是代价呢:在非对称加密方面,我们用了很复杂的数学公式,这就是非对称加密的代价:需要耗费远比对称加密更多的资源来进行加密,而且会根据加密的内容指数上升。
但等等,新的问题来了:你怎么知道你拿到的公钥是真的?
回到刚才的例子。小明打开京东的网页,浏览器拿到了一把「公钥」,准备用它加密银行卡号。但这把公钥真的是京东的吗?
如果小黑提前在网络中间拦截了通信,把自己的公钥伪装成京东的公钥发给小明:
- 小明用「假公钥」把银行卡号加密发出去
- 小黑用自己的私钥解密,拿到了真实卡号
- 小黑再用真正的京东公钥把卡号转发过去
- 小明和京东都毫不知情
这种攻击叫做中间人攻击(Man-in-the-Middle Attack),非对称加密本身无法抵御它。
更现实的问题,想象一下:
- 你的浏览器每天要访问几十个 HTTPS 网站,怎么知道每个网站的公钥是真实的?
- 你没法提前和每个网站「当面确认」公钥,就像当初不能当面约定密钥一样。
非对称加密的核心问题:公钥可以随便传,但没有机制保证这把公钥真的属于它声称的那个人。
这就引出了两个重要概念:签名与验签(证明消息真的来自某人)和证书(证明公钥真的属于某个网站)。
签名与验签
背景
我们回到非对称加密的话题,一般来说一个公钥对应一个私钥。私钥必须保密,而公钥是可以公开的。我们这么想,公钥很多人都知道,但是私钥仅仅只有你知道。那么假如我使用私钥对信息进行”加密”,其他人用公钥”解密”。当然严格意义上来说,签名并不是加密的反向操作,但是为了方便理解,你可以理解公钥与私钥互相对应。那么假如最后信息能被公钥”解密”成功,是不是就相当于你可以明确这条消息一定是拥有私钥的那个人发出来的。这样的操作非常像我们现实生活中的盖章,验证了这个消息一定是我发出来的。
就像你用自己独有的私人印章(私钥)在文件上盖一个章,别人用你的公开印章样本(公钥)来验证这个章是不是真的。私钥负责”盖”,公钥负责”验”。严格来说这并不是加密与解密,因为公钥是公开的。
通用的签名实现方法
RSA-PSS、ECDSA、Ed25519。以下三种算法了解思路即可,实际开发直接调用成熟库,无需自行实现,如果有兴趣可以深入研究。
- RSA-PSS:在 RSA 算法基础上加入随机填充,是目前推荐的 RSA 签名方式,密钥长度一般 2048 位以上。
- ECDSA:基于椭圆曲线的数字签名算法,密钥短、速度快,是比特币、以太坊区块链上最常用的签名算法。
- Ed25519:基于 Edwards 椭圆曲线,设计更现代,签名速度极快且安全性高,是 SSH、TLS 1.3 等新协议的首选算法。
Rust 实现 Ed25519 签名与验签 demo
1 | |
1 | |
和加密对比就能看出签名的底层逻辑与加密完全相反:加密用公钥、解密用私钥;签名用私钥、验签用公钥。
但签名只能证明「这条消息是私钥持有者发出的」,却无法回答「这把公钥真的属于京东吗?」——这就是下一节证书要解决的问题。
证书
证书可以理解为网络世界中的电子身份证,它证明了你就是公钥的拥有者,但注意这并不是意味着说这个网站是安全的。证书仅仅证明公钥确实属于这个网站。在浏览器的地址栏左边,应该可以查看这个网站的证书。
申请证书时会得到的四个文件
当你通过 CA(如 Let’s Encrypt、DigiCert)申请下来一张 SSL/TLS 证书后,通常会得到四个文件:.crt、.pem、.csr、.key。它们各司其职,共同构成证书的完整体系。
| 文件 | 全称 | 内容 | 作用 |
|---|---|---|---|
.key |
Private Key(私钥) | 你自己生成的非对称加密私钥 | TLS 握手时用于解密客户端发来的预主密钥,以及对数据进行签名。绝对不能泄露,只存在服务器上。 |
.csr |
Certificate Signing Request(证书签名请求) | 包含你的公钥 + 域名/组织信息,由私钥签名 | 申请证书时提交给 CA 的文件。CA 核验身份后,会基于 CSR 里的公钥和域名信息签发证书。提交给 CA 后可以删除,但保留也无妨。 |
.crt |
Certificate(证书) | CA 签发的、包含你的公钥 + 域名信息 + CA 签名的 X.509 证书 | 部署在服务器上,TLS 握手时发给客户端,让浏览器验证你的身份。这就是上一节表格里那些字段的实体文件。 |
.pem |
Privacy Enhanced Mail(PEM 编码格式) | Base64 编码的文本文件,以 -----BEGIN ...----- 开头 |
这不是一种证书类型,而是一种编码格式。.crt、.csr、.key 都可以用 PEM 格式存储。很多时候 CA 会把证书链(你的证书 + 中间证书)合并成一个 .pem 文件方便部署。 |
一句话记忆法:
.key→ 你的私钥,放保险箱.csr→ 你提交给 CA 的申请表.crt→ CA 盖章还给你的身份证.pem→ 只是一种文本编码格式,上面三种文件都可能长这个样子
部署时,Nginx 等 Web 服务器通常需要 .crt(或 .pem 证书链)和 .key 两个文件;.csr 仅在申请阶段使用,不需要上传到服务器。
证书都有什么内容

证书遵循 X.509 标准,主要包含以下几个部分:
| 字段 | 说明 | 举例 |
|---|---|---|
| 版本号(Version) | X.509 的版本,目前主流是 V3 | V3 |
| 序列号(Serial Number) | 由 CA 分配的唯一编号,用于标识和吊销 | 0A:1B:2C:3D:… |
| 颁发者(Issuer) | 签发这张证书的 CA 机构名称 | DigiCert Inc |
| 有效期(Validity) | 证书的生效时间和过期时间 | 2025-01-01 ~ 2026-01-01 |
| 主体(Subject) | 证书所有者的信息,即「这张证书是给谁的」 | CN=jd.com, O=京东, C=CN |
| 公钥(Public Key) | 证书所有者的公钥,这是证书最核心的内容 | RSA 2048-bit 公钥 |
| 签名算法(Signature Algorithm) | CA 用来对证书进行签名所使用的算法 | SHA256withRSA |
| CA 签名(Signature) | CA 用自己的私钥对上述所有内容的签名,防止内容被篡改 | 一串二进制数据 |
理解证书的关键:
证书的核心是「把公钥和身份绑定在一起,然后让一个大家都信任的第三方(CA)盖章」。
- CA(Certificate Authority,证书颁发机构) 是全球公认的可信机构,比如 DigiCert、Let’s Encrypt 等。操作系统和浏览器出厂时就内置了这些 CA 的公钥。
- 京东向 CA 申请证书时,CA 会验证京东确实拥有 jd.com 这个域名,然后用 CA 自己的私钥对「京东的公钥 + 域名信息」进行签名,生成证书。
- 你的浏览器拿到证书后,用内置的 CA 公钥验证签名。签名验证通过 → 证书没被篡改 → 证书里的公钥确实属于 jd.com。
这样就形成了一条信任链:你信任 CA → CA 担保了京东的公钥 → 你可以放心使用这把公钥。
证书的信任链
那么如何防止证书的伪造呢?这里就要说到证书的信任链。CA 会在你的系统或者浏览器中内置一些证书,这些就是所谓的根证书,被认为是绝对可信的。然后根证书会签发信息给中间证书,中间证书会签发你自己网站的证书。
验证流程
这是一个自下而上的过程。你自己的证书中的颁发者就是上级证书的名字,从网站证书 → 中间证书 → 根证书,逐层验证:
- 用根证书的公钥验证中间证书的签名(确保中间证书是由根签发的)。
- 用中间证书的公钥验证最终实体证书的签名(确保最终证书是由中间签发的)。
当然还需要检查每个证书的有效期和其他属性,但总体的过程是这样的。
证书的安全性
当然证书也不是完美无瑕,历史上也发生过中间证书的私钥泄漏的事情,以及还有其他安全问题:
① CA 被攻击或行为不端
2011 年,荷兰 CA 机构 DigiNotar 被黑客入侵,攻击者伪造了 google.com、yahoo.com 等数百个域名的证书,用于中间人攻击。事件曝光后,DigiNotar 的根证书被各大浏览器和操作系统紧急撤销,公司随即破产。
这说明:整个信任链的安全性完全依赖于根 CA 的可信度,一旦根 CA 出问题,链条上所有证书都不可信。
② 证书被错误签发
2017 年,赛门铁克旗下的 CA 被发现多次错误签发证书(包括为不存在的域名或未经验证的域名签发),谷歌逐步撤销了对赛门铁克所有证书的信任,最终导致赛门铁克将 CA 业务出售。
③ 私钥泄露
如果网站的私钥泄露,攻击者可以用合法证书冒充该网站,所有浏览器都不会报警。这时需要紧急吊销证书(Certificate Revocation)。
吊销机制有两种:
- CRL(证书吊销列表):CA 定期发布一个「已失效证书」的列表,浏览器下载后对照检查。缺点是更新不及时,列表也越来越大。
- OCSP(在线证书状态协议):浏览器实时向 CA 查询某张证书是否有效。缺点是增加了延迟,且 OCSP 服务器本身可能宕机。
④ 中间人仍然可能发生——CT 日志应对
为了防止 CA 偷偷签发伪造证书,现代浏览器要求所有证书必须记录到公开的**证书透明度日志(Certificate Transparency,CT)**中。任何人都可以查询这个日志,如果发现自己域名被签发了未知证书,说明可能遭到攻击,可以立即申请吊销。
⑤ 一句话总结
证书体系解决了「公钥真伪」问题,但它本身也需要信任基础。现实中通过「根证书内置 + 证书透明度 + 吊销机制」三重保障来维持整个体系的可信度。没有绝对安全,只有持续维护的信任。
HTTPS
HTTPS 可以说是各种证书加解密等内容的集大成者。那么在讲 HTTPS 之前,我们先大概了解一下 HTTP。
HTTP(超文本传输协议)是一种无状态、明文传输的应用层协议,通常运行在 TCP/IP 之上,默认端口 80。它的核心模式是 客户端(如浏览器)发起请求,服务器返回响应。
HTTPS 中的 S 代表 Secure(安全)。它意味着在 HTTP 协议的基础上叠加了 TLS/SSL 安全层。
你可能听说过 HTTP 有三次握手,HTTPS 好像有个四次交互。注意他们不是一种东西。HTTPS 中生成密钥的过程(TLS 握手)与 HTTP 的 TCP 三次握手是完全独立的两件事,但它们在时间上有先后顺序,并且发生在不同的协议层。
- TCP 三次握手:发生在传输层,用于建立可靠的端到端连接。无论上层是 HTTP 还是 HTTPS,都先要完成 TCP 握手。
- TLS 握手:发生在表示层/会话层(通常归类于应用层之下),在 TCP 连接建立之后、HTTP 数据发送之前执行。它的目的是协商加密参数、验证身份、生成会话密钥。
所以一句话来说,TCP 三次握手先完成,TLS 握手在 TCP 连接之后发生。当进行完 TLS 以后,你和服务器之间会通过密钥来进行交互,实现了通信加密的过程。
那么 TLS 握手发生了什么?
第一次交互:Client Hello
客户端会向服务端发送自己所支持的加密套件(简单理解是自己支持哪些加密算法的集合),还有一个随机数。
第二次交互:Server Hello 和证书发送
服务器收到客户端的加密套件以后,选择一项双方都支持的加密套件,然后同时还会发送服务器自己的证书信息,还有服务器生成的一个随机数。
第三次交互:验证证书并发送密钥材料
客户端收到服务器的证书以后会验证证书的有效性(参考上面证书那一节的内容),使用根证书一层层验证服务器的证书有效性。
如果验证不通过,你通常会在浏览器收到警告:显示证书不正确等等信息。你可以选择忽略这个警告。
当证书验证通过或者你忽视了这个警告以后,客户端会生成一个随机数,称为 pre-master secret(预主密钥)。客户端使用证书中的公钥对这个 pre-master secret 进行非对称加密,发送给服务器。服务器用自己的私钥解密,得到 pre-master secret。
此后,双方各自用「客户端随机数 + 服务器随机数 + pre-master secret」在本地推导出相同的会话密钥——这个最终的对称密钥从未完整地出现在网络上,是双方独立计算出来的,而不是直接传输的。
第四次交互:服务器确认并完成握手
服务器用推导出的会话密钥进行对称解密,得到明文,进行处理。至此完成了 TLS 的四次交互。后续的所有消息都会使用这个对称密钥进行消息的加解密。
1 | |
这四个来回(实际上是 2 个 RTT)构成了 TLS 握手的核心,确保了后续 HTTP 通信的机密性、完整性和身份真实性。
HTTPS 的两种攻击形式
中间人攻击
中间人是指,中间有一个代理服务器同时与你的客户端和服务端进行交互。比如你访问京东的网页,客户端就是你的电脑,服务端就是京东。代理服务器同时与两者建立 HTTPS 连接。代理服务器与京东建立 HTTPS 连接很容易。但是要如何让你的客户端信任代理服务器的证书就是一个难点。
在你眼里,你是通过浏览器的地址栏访问京东,结果是代理服务器半路截胡把你拦下来。那么这时候代理服务器的证书实际上并不是绑定的京东的域名,如何让你的电脑相信代理服务器的证书是准确的呢?就需要提前在你的电脑上预埋代理服务器的根证书,那么通过根证书就会为代理服务器的证书签名,这次请求就是合法的。这也是为什么公司会让你安装软件——这样公司就能看到你访问的网页和网页的信息了。
如何判断是否走了代理呢?你可以在浏览器查看证书,如果走代理的话,那里就是代理服务器的证书,并不是你访问的网页真正使用的证书。
偷取对称密钥
TLS 握手完成后,客户端和服务器并没有在网络上直接传递一个完整的对称密钥,而是双方通过交换随机数和密钥材料,各自在本地独立推导出相同的会话密钥。这个密钥从未完整地出现在网络传输中。但如果有软件能读取到内存中这个已推导好的密钥,自然就能解密所有报文。Chrome 浏览器支持通过 SSLKEYLOGFILE 环境变量,将本次通讯的会话密钥写入到电脑某个位置,恶意软件就可以通过这个密钥解密你的报文。
所以安全并不是绝对的,而是相对的。
我们普通人如何保护自己?
① 养成看地址栏的习惯
访问任何需要输入密码或支付信息的网站时,确认地址栏显示的是 https:// 而非 http://。现代浏览器会在不安全的连接旁边显示”不安全”警告,不要忽视它。
② 不要随意忽略证书警告
当浏览器弹出”证书无效””证书已过期””您的连接不是私密连接”等警告时,不要点击”仍然继续”。这类警告往往意味着你正在遭受中间人攻击,或者网站的证书出了问题。
③ 尽量不要在公共 Wi-Fi 下处理敏感信息
公共 Wi-Fi(咖啡馆、机场、酒店)是中间人攻击的高发区。攻击者可以搭建一个同名热点,让你的设备自动连接。如果必须使用公共网络,建议开启 VPN。
④ 警惕公司/学校要求安装的”根证书”
如果有人要求你在设备上安装一个根证书,意味着对方将拥有解密你所有 HTTPS 流量的能力。这在企业内网管理中是正常操作,但如果是来路不明的软件要求这样做,则应高度警惕。
⑤ 及时更新操作系统和浏览器
操作系统和浏览器内置的根证书列表会定期更新,及时撤销已被吊销或不可信的 CA。使用过期系统意味着你的信任链可能包含已经不安全的根证书。
⑥ 使用密码管理器,不要复用密码
即使 HTTPS 保证了传输安全,如果网站服务器端被攻击导致数据库泄露,你的密码仍然可能暴露。为每个网站设置不同的强密码,是最基础的自我保护。
安全是一种习惯,而不是一次性的设置。理解了背后的原理,这些习惯就不再是繁琐的规则,而是有意识的自我保护。