🎯 你正在阅读「网络原理续命手册」系列文章 🎯
🔥 弹简特 个人主页
❄️ 个人专栏直通车:
✨ 靠热爱去书写自己,靠勇敢去书写生活!
🌟 博主简介:

文章目录:
上一篇 我们把 HTTP 的报文、URL、Header、Cookie、状态码分享完了。HTTP 能通信,但数据是明文传输的,容易被窃听和篡改。本篇作为 【JavaSE-网络部分12】,也是本系列最后一篇,讲 HTTPS 如何用加密和证书把 HTTP「包」进安全通道里。
一、前言
HTTPS = HTTP + TLS(老版本常叫 SSL)。HTTP 那套请求/响应格式不变,多出来的是传输前的加密、以及服务器身份的校验。
本文会从对称加密、非对称加密讲起,再说明中间人攻击为什么能得手、数字证书又怎么把公钥「验真」,最后串回 Fiddler 抓 HTTPS 的原理
二、HTTPS
1、HTTPS是什么
HTTPS 本质上给 HTTP 协议添加了加密层。如果没有加密,网络上传输的就是明文,传输的数据很容易被第三方获取,甚至篡改。
这里说的「第三方」,不一定是黑客主动攻击你,也可能是路由器、公共 WiFi、运营商中间设备等——只要数据经过它们时是明文,别人就有机会看到或改动。比如密码、Cookie、银行卡号,在 HTTP 下都可能被直接读出来。
HTTPS 做的事,简单说就是两件事:保密(别人看不懂)和校验身份(确认你连的真是这个网站的服务器,而不是冒充的)。我们平时访问网站,地址栏从 http:// 变成 https://,端口从 80 变成 443,背后走的就是这套机制。
2、加密基础:明文、密文与密钥
网络传输要想保证安全,最核心的方案就是加密。
围绕着加密,会有几个关键的技术术语需要理解:
- 明文:就是要传输的原始数据,表达原本含义。
- 密文:把明文进行一定规则的变化,得到别人看不懂的数据。
明文 → 密文:加密
密文 → 明文:解密
加密和解密的过程中,需要用到特殊的「道具」——这种数据称之为密钥。密钥有两个作用:加密和解密。
其中,加密有两种方式:
2.1 对称加密
加密和解密用的是同一个密钥。好比一把钥匙和一把锁,你这把钥匙既能把它锁上,也能把它打开。
为啥对称加密快? 因为算法相对简单,加解密用的是同一套规则,CPU 算起来省力,所以适合加密一整段 HTTP 正文这种大块数据。HTTPS 里真正扛流量的是它,而不是非对称加密。
2.2 非对称加密
加密使用一个密钥,解密使用另一个密钥。加密和解密使用的密钥是有一定联系的,背后有复杂的数学原理。你使用密钥一来加密,此时就是使用密钥二来解密;也可以使用密钥二加密,此时就是使用密钥一来解密。
由于有两个密钥,所以在实践中通常会把其中一个密钥公开出去,这个密钥称之为公钥;另一个密钥我们自己保存好,不告诉任何人,这个密钥称之为私钥。
通俗理解: 公钥像是一个「只进不出的锁」——任何人都可以拿公钥把数据锁上,但只有持有私钥的服务器能打开。所以公钥就算被全世界知道也没关系,私钥绝对不能泄露。
和非对称对比: 非对称加密数学运算重,加密一大段 HTML、图片、JSON 会非常慢,所以它一般只用来保护很短的内容,比如一把对称密钥、一段校验和。
三、HTTPS的工作原理
问:学了 HTTP,学 HTTPS 要推倒重来吗?
答:不用。 报文格式、URL、Cookie、状态码……上一篇讲的,HTTPS 里全部适用。变的是:这些数据在网线里跑的时候,先被加密了。
问:那 HTTPS 到底怎么一步步做到安全的?
答:可以想成四关,一关一关过,后面每一节对应一关:
| 关卡 | 解决啥 | 结果 |
|---|---|---|
| ① 对称加密 | 大量数据怎么加密 | 有办法了,但密钥交不出去 |
| ② 非对称加密 | 密钥怎么安全交给服务器 | 有办法了,但公钥可能是假的 |
| ③ 中间人攻击 | 公钥被掉包会怎样 | 发现新漏洞 |
| ④ 数字证书 | 怎么证明公钥是真的 | HTTPS 主干成型 |
下面按这个顺序讲。
1、首先最简单的是引入对称加密(第一关)
这里我们最容易懵的地方有三个:密钥谁生成的?客户端发出去时怎么用?服务器返回时又怎么用? 我们一步一步来,先别急着纠结「密钥从哪来」。
第一步:对称加密到底在干什么?
对称加密的意思很简单:加密和解密用的是同一把密钥,所以叫「对称」。
你可以先把它想成一把钥匙:
- 上锁(加密)用这把钥匙
- 开锁(解密)还是这把钥匙
第二步:一次完整的「问—答」,密钥怎么用?
先暂时假设:客户端和服务器手里已经有同一把密钥,叫它 K。至于 K 怎么来的,下一步再说。
下面是一次正常的加密通信(注意:来回都用同一把 K):
① 客户端 → 服务器(发请求)
客户端:用 K 把「明文请求」加密 → 得到「密文请求」→ 发出去
服务器:收到密文 → 用同一把 K 解密 → 得到明文请求
② 服务器 → 客户端(回响应)
服务器:用 K 把「明文响应」加密 → 得到「密文响应」→ 发回去
客户端:收到密文 → 用同一把 K 解密 → 得到明文响应
所以别搞混了:
- 不是客户端一把钥匙、服务器另一把钥匙
- 而是双方共享同一把 K,谁发数据谁加密,谁收数据谁解密
- 客户端发给服务器的用 K 加密;服务器返回给客户端的,也用 K 加密,如下图:

图中,路上就算有黑客截获,他看到的只是密文。没有 K,解不开——前提是黑客不知道 K 是啥。
第三步:那密钥 K 到底是谁生成的?
这才是真正绕人的地方。常见就两种思路:
| 方式 | 谁生成 K | 典型做法 |
|---|---|---|
| A. 大家共用一把 | 往往服务器定好,或写死在程序里 | 所有用户装同一个 APP,里面内置 K=888888 |
| B. 每个客户端各用一把 | 客户端自己随机生成 | 你这次连上来随机一个 K,跟别人的不一样 |
HTTPS 后面走的是 B 的思路:每次连接,客户端(和服务器协商后)会用到本次会话专用的对称密钥,不是全世界共用一把。
但不管 A 还是 B,都有一个绕不过去的坎:
不管谁生成的 K,总得让对面也知道 K 是多少,不然对方解不开密文。
- 若是 A(共用一把):每个新客户端连上来,总得拿到这把 K(内置、下载、或服务器告诉都行)
- 若是 B(各用各的):客户端自己生成了 K,就得告诉服务器:「我这次用 K=xxxx」
「告诉对方密钥」这一步,就是后面所有问题的起点。
第四步:方案一 —— 所有客户端共用一把 K
乍一听省事:全站用户都用 K=888888,服务器也只记这一把。
但你想:每个用户第一次打开 APP、第一次访问网站,总得 somehow 拿到这把 K 吧? 常见做法:
- 写死在安装包里;或
- 第一次连接时,服务器明文发给客户端
那黑客能干嘛?他不用黑服务器,只要自己写个程序,假装成普通客户端去连你的服务器。服务器分不清真假——对服务器来说都是在「发密钥给客户端」。黑客一旦也以客户端身份拿到了 K,路上所有密文他都能解开。
所以共用一把 K,等于谁都能当客户端,谁就都知道 K,不是偶然泄露,是设计上就守不住。
第五步:方案二 —— 每个客户端各自随机生成 K
这种方案相比于方案一就合理多了:A 用户随机 K1,B 用户随机 K2,互不干扰。
但是现在新的问题又来了,你看,我是让每个客户端密钥都不同了,此时我就需要想办法把密钥传输给服务器,此时你得告诉服务器,我的密钥是啥?不然你服务器不知道你客户端的密钥的话,他无法对你的数据进行解析,如下图所示:

客户端对服务器说,我的密钥是 888888,然后我先把这 888888 告诉你,后面我再将我的数据通过这个 888888 加密发给服务器,服务器最终通过 888888 来解密我的数据。
但是这种方案很显然是不行的,因为我们的密钥是明文传输,黑客能够拿到你的密钥信息,它可以通过密钥来解析你的数据,所以这种方案不可行。
有人可能会想:那我用另一把密钥 K2 把 888888 加密了再传行不行?
还是不行。K2 总得明文交给服务器吧?黑客截获 K2,888888 照样泄露。这就是「套娃」——不管包几层,最外面那把钥匙总得裸奔一次。
所以:单靠对称加密,解决不了「第一次见面,怎么安全地把密钥交给对方」这个问题。
小结(对称加密这段):
- 来回通信用同一把 K,客户端发、服务器回,都靠它加解密
- K 要么共用,要么客户端随机生成,但总得让对面知道
- 「告诉密钥」这一步没法安全完成 → 只上对称加密,搞不定 HTTPS
接下来引入非对称加密,专门解决「怎么把密钥安全交给服务器」这件事。
2、引入非对称加密(第二关)
第一关我们把坑踩透了:对称加密本身没问题,卡就卡在「密钥怎么安全交给服务器」。第二关要回答的就是这件事。
这里同样最容易懵的地方也有三个:非对称到底多了什么本事?为啥不能全程用它?正常数据和传密钥为啥要分开处理? 还是一步一步来:
第一步:非对称加密,比对称多了什么?
第一关里,加密和解密用的是同一把密钥。非对称不一样——它有两把配对的密钥:公钥和私钥。
你可以先记住它最常用的一种用法(HTTPS 握手阶段就靠这个):
- 用公钥加密的东西,只有私钥能解开
- 公钥可以公开,谁都能拿;私钥只有持有者自己留着
那这跟第一关的卡点有啥关系?
第一关是:客户端得告诉服务器「咱俩这次用 key=888888」,但这句话只能明文说,黑客能看见。
非对称提供了一种新可能:客户端可以用服务器的公钥,让后用公钥把这个key=888888 锁起来再发出去。 路上黑客截获了,他手里没有服务器的私钥,于是黑客就打不开这把锁——密钥就不用裸奔了。
第二步:那能不能干脆全程用非对称?
既然非对称这么厉害,能把 key 安全送过去,那 HTTP 正文、图片、JSON……也全用它加密行不行?
你先自己想想。 一张网页、一个接口返回,动不动几 KB、几 MB。非对称加密每算一次都慢,全程用它扛流量,服务器和浏览器都会算不动。
对称加密呢?第一关已经看到了——快,适合大块数据。所以现实里不是「二选一」,而是各干各擅长的事:
- 大批量 HTTP 数据 → 还是对称加密(第一关第二步那套,来回用同一把 key)
- 短短一串对称密钥 key → 交给非对称来保护(只在这一下「第一次见面」用)
这就是后面要说的混合加密。不是拍脑袋定的,是第一关的坑 + 性能,两边一起推出来的。
第三步:混合加密到底怎么组合?
第一关留下了问题:key 没法安全交给服务器。
第二步又排除了:全程非对称不现实,太慢。
那怎么办?==》针对正常传输的数据,使用的还是对称加密;但是针对对称密钥传输的时候,使用非对称加密。
说白了就是:
| 传什么 | 用什么 | 为啥 |
|---|---|---|
| HTTP 请求/响应正文 | 对称加密(key) | 快,适合大量数据——第一关第二步已经会了 |
| 对称密钥 key 本身 | 非对称加密(公钥/私钥) | 只传短短一串,慢点没关系;关键是路上偷不走 |
注意: 不是「HTTP 用对称、密钥用非对称」两套互不相关的系统,而是先靠非对称把 key 安全送到服务器,再靠对称用这把 key 加密后面的 HTTP——前后是接在一起的。
第四步:完整走一遍——key 怎么安全交到服务器手里?
下面按时间顺序走,我们每一步都会先给一个示意图
① 服务器先生成一对密钥
- 公钥
public1:可以给任何人 - 私钥
private1:只有服务器自己留着

② 客户端从服务器拿到公钥
公钥本来就是设计为公开的,明文传公钥没问题——就算黑客看见了,他也只是知道「锁长啥样」,没有私钥照样打不开用公钥锁起来的东西。

③ 客户端本地随机生成对称密钥 key
就是第一关方案二里那个思路:你这次连上来,随机一把 key,跟别的用户不一样。举例:key = 888888(此时还在客户端本地,还没上网)。

④ 客户端用「公钥」加密 key,发给服务器
这一步是关键——发出去的不是明文 888888,而是用公钥 public1 锁起来后的密文 Enc_public1(888888)。路上是密文。

⑤ 服务器用「私钥」解密,拿到 key
黑客就算在中间截获了第 ④ 步的密文,他没有 private1,解不开。服务器用 private1 解密 → key = 888888 到手。

⑥ 之后就和第一关第二步一样了
上述服务器已经知道你客户端的密钥是888888了,而且这个密钥是加密传输给服务器的,你黑客不知道,所以接下来:
- 客户端用 key(888888) 加密 HTTP 请求 → 服务器用 888888 解密
- 服务器用 888888 加密 HTTP 响应 → 客户端用 888888 解密

第五步:你可能还会问的两个问题
可能有疑问:如果黑客入侵了服务器,不就拿到私钥了吗?
这个问题不太现实。因为如果黑客都有能力入侵服务器了,他直接读数据库、读文件就行,不必费劲在路上抓包。HTTPS 主要防的是「路上的黑客」,不是「服务器已经被攻破」。
那么上述看起来应该能实现了,但是有一个疑问:既然非对称加密很美好,为什么还要保留对称加密呢?
因为对称加密,加密解密的过程效率非常之高【适合数据量大的】;而非对称加密,加密解密的效率非常低【适合加密小的数据】。所以针对后续大批量的业务数据传输,使用的还是对称加密;而对于对称密钥,使用非对称加密,因为对称密钥的业务数据并不长,很适合用非对称加密来加密。
第二关小结:
- 第一关卡在 key 没法安全交给服务器 → 非对称用「公钥锁、私钥开」补上这一块
- 全程非对称不现实 → 大批量 HTTP 仍用对称
- 混合加密 = 非对称传 key + 对称传数据,前后接在一起
上述的过程看起来很美好,但是还有一个非常严重的漏洞,这个漏洞称之为中间人攻击。
3、中间人攻击(第三关)
第二关走下来,逻辑很顺:用 public1 把 888888 锁起来传,路上黑客没有 private1,解不开——密钥安全送达,混合加密成立。
上述的过程看起来很美好,但是还有一个非常严重的漏洞,这个漏洞称之为中间人攻击。
第三关要回答的就是:第二关到底漏了哪一块?黑客怎么又把 key 偷走了?
这里同样最容易懵的地方也有三个:攻击是在破加密算法吗?为啥客户端和服务器都察觉不到?公钥从网上拿来就能信吗? 还是一步一步来。
第一步:第二关漏了哪一块?
先别急着看图,你想一想第二关②步:客户端是怎么拿到 public1 的?
答:是从网上问服务器要来的——客户端发一句「你的公钥是啥?」,对面回什么,客户端就先信什么。
第二关只解决了:key 用公钥锁起来传,路上偷不走明文。
但还没解决:你拿到的这把「公钥」,真是服务器的吗?
客户端此刻没有能力验证「这是百度服务器的 public1,还是黑客编的 public2」。只要黑客夹在中间,把回包掉包,第二关那套加密流程照样能走通——只是你锁钥匙用的,已经不是你以为的那把锁了。
所以:中间人攻击不是把加密算法攻破了,而是攻破了「你以为在和谁说话」这件事。
第二步:中间人攻击像什么?——电影《毒战》
这个中间人攻击,和我们的一部电影叫做《毒战》非常相似,由古天乐和孙红雷扮演。
先简单说说剧情背景(没看过的同学也能听懂):
孙红雷演的是缉毒警察张静,古天乐演的是毒枭李志雷——一个极其狡猾的制毒贩毒头目。警察要端掉这条线,不能光靠硬冲,得想办法盯住、参与、甚至「介入」毒贩们的那笔大交易:什么时候交货、谁跟谁接头、货和钱怎么转手,都得摸清楚。
电影里那种「交易」并不是两个人当面一手交钱一手交货那么简单。毒贩为了安全,往往层层转手:
- 真正的买家和真正的卖家,未必直接见面
- 中间有马仔、司机、接头人来回传话、传货
- 每一环的人都可能跟下一环说:「放心,对面就是老熟人」
于是就会出现这种局面:买卖双方都觉得谈判还在正常推进,报价、时间、地点都对得上,气氛也像那么回事。但中间某一环被人买通、冒充、或者干脆就是警察/对手安插的人——货在中途被换了包,钱也可能进了别人的口袋;消息传到你耳朵里时,早就被过滤、篡改过了。等你反应过来,真正和你成交的,早就不一定是原来那个人了。
《毒战》里这种紧张感,很大一部分就来自:你以为在跟 A 谈,其实中间经过 B、C、D 转手,你根本看不清全貌。古天乐演的毒枭尤其擅长利用这套「中间环节」跟你绕圈子;孙红雷演的警察,也要在这些环节里抢时间、抢信息——谁控制住了「中间那一段」,谁就能占上风。
这跟中间人攻击简直是一个模子:
中间人攻击也是这个味道:
- 客户端以为自己在和服务器谈「咱俩用哪把公钥锁 key」
- 服务器以为自己在和客户端谈
- 实际上中间站着黑客,两边瞒着对方偷偷转发——表面交易还在进行,中间却换了人、掉了包
就像《毒战》里那笔交易:买卖双方都觉得「对面就是我要找的人」,流程也在走;但关键的东西(货、钱、口令)经过中间人时,已经被掉了包。HTTPS 里掉包的是公钥——客户端以为拿到的是服务器的 public1,其实是黑客的 public2。
中间人攻击是啥? 就是黑客夹在你和服务器中间,不正面硬破加密算法,而是:
- 假装自己是服务器,跟客户端说话
- 假装自己是客户端,跟服务器说话
- 关键信息(尤其是公钥)在中途被掉包
你以为在和银行服务器建立 HTTPS,其实第一句「公钥是啥」的回包,可能已经被黑客换成了他自己的 public2。

第三步:动手之前——各方手里有什么?
跟第二关一样,先把角色和密钥摆清楚(下面举例仍用 888888 当对称密钥):
| 角色 | 手里有什么 |
|---|---|
| 服务器 | public1 + private1(第二关①步生成的,长期用) |
| 客户端 | 暂时还没有 key,等会儿要随机生成 888888 |
| 黑客 | 暂时没有,等会儿会自己生成一对 public2 + private2 |

第四步:完整走一遍——黑客怎么偷到 key?
下面跟第二关④步一样,咱们一步一步来,慢着读,对着图看。
你可以先建立一个对比框架:中间人攻击的流程,和第二关长得几乎一模一样——服务器生成密钥、客户端要公钥、用公钥锁 key、服务器用私钥解开、再用 key 加密 HTTP……
唯一致命的差别:客户端拿到的公钥,可能不是 public1,而是黑客的 public2。
下面按时间顺序走。每一段先讲「发生了什么」,再点「和第二关比差在哪」。
首先:服务器生成密钥对
首先呢,由服务器生成一对公钥和私钥,公钥是 public1,私钥是 private1。
这跟第二关①步完全一样——服务器开服时就生成好了,长期用;对称密钥 888888 仍是每次连接由客户端随机生成,跟 public1/private1 不是一回事。
此时还没有黑客什么事。服务器手里有:
public1:待会儿要发给客户端的private1:待会儿用来解开「被 public1 锁住的 key」

然后:客户端来要公钥
然后客户端会问服务器:「咱们的公钥是啥?」然后我们的服务器就会返回公钥的内容——也就是 public1。
到这一步还正常。 第二关②步走的也是这条路:公钥明文传输,路上看见 public1 没关系,因为光有公钥解不开用公钥锁起来的东西。
但隐患已经埋下了:客户端是从网上问一句、对面回一句就收下公钥的——它还没办法核实「对面真是服务器本人」。
在没黑客的情况下没问题;一旦有人夹在中间,这一问一答就是掉包的机会。

核心:黑客掉包公钥
此时核心就出现了。
我们的黑客自己也生成了一对非对称密钥,public2 和 private2——跟服务器生成 public1/private1 一样,谁都能造一对,数学上不费事。
差别在于:public1 是服务器身份对应的公钥,public2 是黑客身份对应的公钥。
黑客拦截服务器本要发回的 public1,不交给客户端,而是把 public2 塞过去,冒充服务器公钥。
从而客户端被阴了一手——他还以为这个公钥是我们服务器的公钥呢,完全并不知道被黑客给替换了。
服务器那边也蒙在鼓里,它以为自己已经把 public1 发出去了。
对比第二关②步: 那时客户端拿到的是真 public1;现在拿到的是假 public2——后面所有「用公钥锁 key」的操作,锁都锁错了。


客户端用错的公钥锁自己的对称密钥key
于是此时客户端就会基于这个 public2,对对称密钥 key 进行加密(举例 key = 888888),准备把对称密钥的密文传输给服务器。
从客户端视角,它觉得自己还在按第二关④步做:本地随机了 key,用「服务器公钥」锁起来,发出密文——逻辑上没毛病。
但第二关④步是 888888 + public1 → Enc_public1(888888),只有 private1 能开;
现在变成了 888888 + public2 → Enc_public2(888888),能开的是 private2——在黑客手里。
加密算法没坏,是锁挂错了门。

黑客解开密文,key 到手
此时我们的黑客就会使用自己的私钥 private2 对上述的密文进行解密,此时黑客就拿到了对称密钥 key。
这一步,在第二关里本该是服务器用 private1 解密、拿到 888888 的地方——现在解密的人换成了黑客。
所以密钥交换这一步,黑客已经赢了:第二关⑤步想防的「路上偷 key」,防的是偷密文;但密文既然是用 public2 锁的,黑客本来就有钥匙。

黑客转手:用真的 public1 发给服务器
与此同时,黑客会继续使用刚才服务器的公钥 public1,对拿到的对称密钥 key 重新加密,并传给服务器。
你可能会问:黑客为啥不就此停手?
因为如果服务器拿不到 key,后面 HTTP 没法正常聊,客户端很快会发现「不对劲」。黑客得把戏演完——假装什么都没发生。
所以它用真的 public1 把 888888 再锁一遍,发给服务器。服务器用 private1 解密,同样呢也能拿到我们的对称密钥的数据——在服务器看来,客户端就是按第二关正常流程发来的。


服务器也拿到 key,握手看似成功
然后服务器拿到这个对称密钥之后,使用私钥 private1 解密——注意:这里解的是「key 的密文」,也就是黑客转发过来的 Enc_public1(888888),解出来得到 key = 888888。

此处有一个容易混的地方: 服务器返回给客户端的「确认」,还用不用 private1?
不用了。 private1 的任务到此为止——它的活儿就是解开「被 public1 锁住的 key」。
key 到手之后,双方已经有了同一把对称密钥 888888,后面的确认和业务数据,都走对称加密(跟第二关⑥步一样)。
所以服务器返回的内容,可以通俗理解成:用 888888 加密的响应,意思是「好滴好滴,我已经知道你的密钥了」——客户端用 888888 解密能看懂,黑客此时手里也有 888888,同样能解密、能转发。

真实 HTTPS(TLS)里不会真的传「好滴好滴」这几个字,而是规范的握手完成报文(比如 Finished);这里是为了好懂,用口语打个比方。
这条确认经黑客转手回到客户端。客户端用 888888 解密,一看:对面确认收到了 → 更坚信密钥交换成功了。
两边都以为密钥交换成功了——跟第二关⑥步表面上一样。此时:
- 客户端有
888888 - 服务器有
888888 - 黑客也有
888888
客户端和服务器都察觉不到中间多了一人,因为黑客两边都在帮忙转发——key 的密文它转发,确认响应它也转发,握手流程走完了。
业务数据:加密了,但对黑客没用
此时我们客户端就知道了服务器已经收到对称密钥了,那么接下来客户端的请求都是使用对称密钥 key=888888 进行加密的。
这跟第二关⑥步、第一关第二步同一条路:HTTP 请求、响应,来回都用 888888 对称加密,量大、快。
但第二关和第三关在这里分道扬镳:
| 第二关⑥步 | 第三关(这里) | |
|---|---|---|
黑客有没有 888888 | 没有(没 private1,解不开 key 密文) | 有(第④步已用 private2 偷到手) |
| HTTP 密文对黑客 | 看不懂 | 看得懂、能改 |
而我们的黑客手里已经有对称密钥 key 了,此时业务数据虽然是加密的,但是形同虚设——跟第一关「明文传 888888 之后的数据裸奔」效果一样,只是多绕了几圈。

收束:这就叫中间人攻击,漏洞在哪?
整个过程中,我们的服务器和客户端都不知道黑客的存在。上述这样的一个过程,就称之为中间人攻击。
为啥双方察觉不到?
因为黑客解密后会用真的 public1 再加密转发给服务器,服务器侧握手成功;客户端侧也收到「正常」响应。表面交易还在进行——就像《毒战》里那笔货还在转,但中间早就换了包。
整个过程中的关键在于:
客户端无法区分收到的公钥是服务器的
public1,还是黑客伪造的public2。
第二关只保证了「key 被公钥锁住,路上解不开」——没保证「你手里的公钥真是服务器的」。只要公钥能掉包,混合加密整段逻辑都会被绕过去。
想解决这个问题,得让客户端能证明:「这个公钥真是这个网站服务器的,不是黑客编的。」——比如引入证书,由权威第三方来盖章验真。
那么如何解决中间人攻击问题,我们就要引入一个机制,这个机制叫作校验机制。
第三关小结:
- 不是算法被攻破,是公钥身份没被验证
- 掉包的是公钥(
public1→public2),偷走的是对称密钥 key - 流程和第二关几乎一样,所以特别隐蔽——双方握手都「成功」,只有黑客能看、能改 HTTP
4、引入证书机制(第四关)
第三关把坑挖透了:公钥是从网上要来的,可能是黑客的 public2,客户端却没法分辨。
那么如何解决中间人攻击问题,我们就要引入一个机制,这个机制叫作校验机制。
第四关要回答的就是:怎么让客户端相信「这把公钥真是这个网站的」,而不是黑客编的?
这里最容易懵的地方也有三个:证书是啥?数字签名怎么防伪造?客户端验证书那三步分别在干啥? 还是一步一步来。
第一步:第三关的坑,证书要怎么填?
先回忆第三关收束时的那句话:
客户端无法区分收到的公钥是服务器的
public1,还是黑客伪造的public2。
那最直觉的想法是:让服务器自己发誓——「我发誓,我的公钥是真的!」
行吗?不行。 第三关已经演示过了:黑客冒充服务器时,也能同样发誓。自己说自己可信,等于没说。
我们需要的是:来一个客户端事先就信任的第三方,核实之后盖个章:「我查过了,www.xxx.com 用的就是这把 public1。」
这份盖了章的证明材料,就是数字证书。
第二步:第三方是谁?——CA(证书颁发机构)
这个第三方,有个专门叫法:CA(Certificate Authority,证书颁发机构)。下文说的「公证机构」,指的就是它。
常见 CA 有 Let’s Encrypt、DigiCert 等。你可以把它想成办护照时的派出所、公证处——不是你自己说「我是我」,而是权威机构在材料上盖章,别人才认。
这里面的核心就是有一个第三方公证机构,它足够权威,足够说了算。那么它说公钥是正常的,此时客户端就可以相信这个公钥就是服务器给我返回回来的。
为啥必须靠 CA? 因为中间人也能伪造「自证」。客户端需要的是:只认 CA 的章,不认服务器空口白话。

操作系统和浏览器出厂时就内置了一批「我信任的 CA 名单」——验证书时用的 CA 公钥 public3 从这份名单来,不是临时从网上要的(这一点后面「四个问题」第 4 问会再讲)。
第三步:服务器怎么申请证书?
我们的服务器在最开始开服的时候,就需要去公证机构里面申请对应的证书。那么和我们生活中类似,你去申请一个东西,我们是需要带一些材料的——比如办护照,得带身份证或户口本。
那么服务器申请证书也需要携带一些材料,比如你服务器生成的公钥是啥?服务器的域名(身份标识)。
| 要交的材料 | 为啥要交 | 注意 |
|---|---|---|
域名(如 www.baidu.com) | 证明「我是这个网站」 | 黑客拿不到别人域名的合法证书 |
| 公钥 public1 | 证明「我以后用这把公钥跟用户通信」 | 私钥 private1 不交出去! |
CA 审核通过后,发回一张数字证书(本质就是一段有固定格式的字符串)。
两个容易混的点:
- 对称密钥
888888:每次连接客户端随机生成,会话结束就丢,经常换 - 服务器 public1 / private1:生成一次,配在证书里,长期用
证书干的事就是把 域名 和 public1 绑在一起——客户端验的是「www.baidu.com 的公钥,是不是 CA 盖章确认过的那把」。

第四步:数字证书里到底有什么?
公证机构返回的数字证书包含下列内容:
- 公证机构的身份标识(哪个 CA 发的)
- 证书的有效期(啥时候过期)
- 公钥 public1
- 证书的所有者(服务器域名)
- 证书的 数字签名(最关键)
它其实就是一个字符串,只不过被划分为多个部分,每一部分有自己的含义。
其中最重要的部分就是数字签名。证书会不会被伪造?关键就在数字签名——它确保了证书无法被随便篡改。

第五步:数字签名是怎么「盖章」的?
上面看了证书结构,最后一格写着 数字签名——我们可能会卡在这里:它到底是啥?跟 public1、CA 的 public3/private3 啥关系?后面验证书时的「校验和 1、校验和 2」又从哪来?
别慌,就记一句:CA 给证书盖了一个拆不掉的章。下面用三步说完。

先分清两对密钥
前面第二关讲过:服务器有自己的 public1 / private1。
到第四关,CA 公证机构自己也有一对密钥——咱们叫它 public3 / private3:
| 密钥对 | 谁的 | 干啥 |
|---|---|---|
| public1 / private1 | 服务器 | public1 写进证书,用来锁 / 解对称密钥 888888 |
| public3 / private3 | CA 公证机构 | CA 用 private3 给证书盖章;客户端用 public3 验章 |
跟服务器一样,CA 的密钥也是生成一次、长期用。区别是:
private3:只有 CA 自己留着,绝密,专门用来生成数字签名public3:不写在这张网站证书里,而是预装在你电脑 / 浏览器的「受信任 CA 列表」里(第六步会讲)
所以第五步讲的「盖章」,盖的是 CA 自己的 private3;跟证书里的 public1 是两套钥匙,别混。
打个比方:合同 + 手印
签合同要签名字 + 按手印——正文大家都能看,改一个字就对不上手印。数字签名是同一思路,只不过用 SHA-256 算校验和代替手印:
| 步骤 | 干啥 |
|---|---|
| ① 算校验和 | 对证书正文(域名、public1、有效期……)用 SHA-256 算出一个数 |
| ② 盖章 | CA 用 private3 在这个校验和上加密,得到数字签名,贴在证书末尾 |
| ③ 验章 | 客户端用内置的 public3 解开签名,看跟自己对正文算的校验和一不一致(第六步细讲) |
图示:



容易混的一点: 算校验和时只算正文,不算末尾的数字签名——签名是盖完章才贴上去的。黑客把 public1 换成自己的公钥,正文一变,校验和全变,对不上 CA 当初盖的章。
CA 为啥不整份证书都用 private3 加密?因为域名、公钥本来就要公开给客户端看,只能对校验和签名,相当于在公开合同上按手印,不是把整份合同锁进保险柜。
串起来(CA 签发时)
- 拼好证书正文(域名 +
public1+ …) - 对正文做 SHA-256,得到校验和
- CA 用
private3对校验和加密,得到数字签名,贴在证书末尾

一句话: 数字签名 = CA 用 private3 对「证书正文校验和」盖的章。证明这份正文真是 CA 审过的,不是黑客自己编的。
留给第六步: 客户端拿到证书后,自己再算一遍正文校验和(校验和 1),用电脑里内置的 public3 解开数字签名得到 CA 当初的校验和(校验和 2)——两个相等,才信证书里的 public1。
第六步:从头到尾走一遍——从服务器申请到客户端验证书
这一节最容易整段糊在一起。咱们不要一上来就「校验和 1、校验和 2」,先把时间线摆清楚:
| 阶段 | 什么时候发生 | 发生几次 | 干啥 |
|---|---|---|---|
| A | 网站开服前 | 一次 | 服务器生成密钥 → 向 CA 申请 → 拿到证书并部署 |
| B | 你买电脑 / 装系统时 | 一次 | 电脑里内置受信任 CA 列表(含 public3) |
| C | 你点 https 链接时 | 每次访问 | 要证书 → 验签 → 通过后才走之前的第二关 |
下面按 A → B → C 顺序走。三把钥匙先记牢,别混:
| 钥匙 | 谁的 | 从哪来 | 干啥 |
|---|---|---|---|
| public1 / private1 | 服务器 | 阶段 A 生成,public1 写进证书 | 锁 / 解对称密钥 888888 |
| public3 / private3 | CA | public3 出厂内置在你电脑(阶段 B) | 验证书上的数字签名 |
| 888888 | 本次会话 | 阶段 C 客户端随机生成 | 加密 HTTP 正文 |

阶段 A:开服前(服务器 + CA,只做一次)
A-① 服务器生成 public1 和 private1
跟第二关①步一样:服务器先生成一对非对称密钥,长期用,不是每次访问都换。
- public1:将来写进证书,给全世界看
- private1:只有服务器留着,用来解「被 public1 锁住的 888888」
此时还没有证书,也还没有用户来访问。

A-② 服务器向 CA 申请证书
服务器像办护照交材料一样,带上两样东西去找 CA:
- 域名(如
www.example.com)——证明「我是这个网站」 - 公钥 public1——证明「我以后用这把公钥跟用户通信」
private1 不交。 私钥永远留在服务器自己手里,CA 也看不到。
CA 审核:域名是不是你的、材料齐不齐。通过后进入下一步。

A-③ CA 签发证书,服务器部署
CA 签发证书,就是第五步讲的那三件事,分开说:
- 拼证书正文:把域名、
public1、有效期、CA 是谁……写在一起 - 算校验和:对这份正文做 SHA-256,得到一个数
- 盖章:CA 用自己的 private3 在这个校验和上加密,得到数字签名,贴在证书末尾
最后交给服务器的是一张数字证书 = 正文 + 数字签名。
服务器把它配置到网站上。以后用户来访问,服务器出示的就是这张带 CA 章的证书,而不是空口说一句「我的公钥是 public1」。

阶段 B:你的电脑出厂时(只做一次)
你可能最想问的一句:客户端是不是出厂就有一个公证机构的公钥?
答:对。 更准确说:你的 Windows / macOS / Android / Chrome 内置了一份「受信任的根证书列表」,里面就有 DigiCert、Let’s Encrypt 等各大 CA 的 public3(以及对应根证书信息)。
| 问题 | 答案 |
|---|---|
| public3 是上网现要的吗? | 不是,装系统 / 装浏览器时写进本地 |
| 黑客能像第三关掉包 public1 那样掉包 public3 吗? | 不能(在这次连接里),因为根本没走网络 |
| 列表会变吗? | 系统更新可能增减;你手动安装 Fiddler 证书 = 多加一个你信任的 CA |
所以:阶段 B 在你第一次访问任何 https 网站之前就完成了。 验证书时用的 public3,来自自己硬盘里的名单,不是服务器发给你的。

阶段 C:你点 https 链接时(每次访问)
下面假设你访问 https://www.example.com,服务器已在阶段 A 部署好证书,你的电脑已在阶段 B 内置好 CA 的 public3。
C-① 向服务器要「证书」,不要裸公钥
客户端不再问「公钥是啥」(第三关的坑),而是问:「把你的证书给我。」
证书里已经包含 public1、域名、有效期、数字签名等。
你可能还在问:要证书、返证书,路上是明文还是密文?黑客看见了会怎么想?
这两步发生在 TLS 握手前半段——888888 还没谈拢、通道还没加密,所以:
| 动作 | 明文还是密文 | 黑客能看见啥 |
|---|---|---|
| 客户端要证书 | 明文 | 「有个客户端在连这个站,马上要证书了」 |
| 服务器回证书 | 明文 | 证书全文:域名、public1、数字签名…… |
我们传输的内容黑客看得见么?
没错,黑客都能看见。它会不会想:「行,先让你把证书拿到手再说,后面再动手」?
那么在里面黑客会获取到证书吗?
会——但让它先把真证书交到你手上,恰恰是安全的。 证书本来就是公开资料(就像营业执照挂橱窗里,不怕路人看)。第四关防的不是「证书被偷看」,而是「公钥被人掉包成假的还能骗过你」。
黑客拿到证书之后,常见几种打法:
| 黑客的打法 | 结果 |
|---|---|
| 原样转发,不碰证书 | 你验签通过 → 用真 public1 锁 888888 → 黑客没有 private1,解不开(跟第二关「看见公钥没用」一样) |
改证书里的 public1,换成 publicHACK | 正文变了 → 后面 C-④ 校验和对不上 → 浏览器报红 |
| 整张换成假证书,自己画一份签名 | 没有 CA 的 private3,章是假的 → 验签不过 |
第三关裸公钥可以悄悄掉包;有了证书 + 验签,敢改就露馅,敢伪造就报红——所以 C-②~C-④ 才是重头戏。


客户端得到证书做以下三件事:
- 针对证书的各个属性,再次计算校验和,得到校验和1
- 客户端使用公证机构的公钥public3,对收到的数字签名进行解密,得到了校验和2。
- 客户端进行对比,校验和1和校验和2是否相等?
接下来一步一步说:👇
C-② 自己算校验和 1
验证书时会出现两份校验和:
| 叫法 | 谁算 / 从哪来 |
|---|---|
| 校验和 1 | 客户端自己对证书正文再算一遍 |
| 校验和 2 | 用 public3 解开数字签名,取出 CA 当初盖章时留下的那份 |
正常情况 校验和 1 == 校验和 2——说明是同一份正文算出来的。下面先算第一份:
客户端拆开证书,读出正文部分(域名、public1、有效期……不含数字签名),用跟 CA 签发时完全相同的 SHA-256 公式再算一遍。
「我手里这份证书正文,校验和应该是多少?」——这就是 校验和 1
若黑客把证书里的 public1 改成 publicHACK,这里算出的校验和就会变。

C-③ 用内置 public3 解数字签名 → 校验和 2
客户端从证书末尾取出数字签名,用阶段 B 内置在自己电脑里的 CA 公钥 public3 去解密,得到 CA 当初盖章时留下的校验和。
「发证机关认可的校验和,应该是多少?」——这就是 校验和 2
为啥是 public3 解密? 因为签名是 CA 用 private3 加出来的,只有配对的 public3 能解开——跟第二关「public1 加密、private1 解密」同一套非对称逻辑。
为啥不用问服务器要 public3? 因为 public3 在本地信任列表里,不是这次连接网上要的——这就是第四关能防住「对 CA 再来一次中间人」的原因。

C-④ 校验和 1 和 2 相等吗?
| 结果 | 说明 |
|---|---|
| 校验和 1 == 校验和 2 | 正文没被改,且真是这个 CA 签的 → 信任证书里的 public1 |
| 不相等 | 被篡改、伪造,或 CA 不受信任 → 浏览器报红 |
如果相等,那么此时我就知道:服务器的公钥是:public1了

如果校验和相等,说明证书是合法的;如果校验和不相等,说明有人对证书进行篡改了(此时很可能就是公钥被变了,那公钥就不是服务器最初的公钥)。于是客户端就会弹出提示,告诉用户当前的访问存在风险。
比如浏览器大红页「您的连接不是私密连接」——常见原因之一就是证书验不过。有人赶紧关,有人点「继续访问」——那是你愿意自担风险,哈哈哈哈。

C-⑤ 验过了,接下来传对称密钥
只有 C-④ 通过,客户端才相信:证书里的 public1 真是 www.example.com 的、真是 CA 盖过章的。
公钥身份确认完毕。 下面按之前顺序继续:
① 客户端随机生成本次连接的对称密钥
本地生成一把 key,比如 key = 888888。此时还在自己电脑里,还没发出去。
② 用公钥 public1 加密 key,发给服务器
用刚验过的 public1 把 888888 锁起来 → 得到 Enc_public1(888888),发出去。

路上是密文——黑客就算截获,没有 private1 也解不开。

③ 服务器用 private1 解密,拿到 888888
private1 只有真服务器有(开服时生成,申请证书时也没交给 CA)。解开密文 → key = 888888 到手。
到这一步,客户端和服务器都握着同一把 888888 了。服务器再往回发东西——握手确认、后面的 HTTP 响应——一律用 888888 对称加密成密文,不再用 public1 / private1 了。

④ 双方用 888888 加密 HTTP 正文
客户端发请求:用 888888 加密 HTTP 正文 → 密文发出。
服务器收请求:用 888888 解密 → 得到明文。
服务器回响应:同样用 888888 加密 → 密文发出。
客户端收响应:用 888888 解密 → 得到明文。
黑客不知道 key,路上截到的请求、响应都是乱码,读不懂。

阶段 C 验证书三步,一句话串起来:
拿到证书,自己算校验和 1,用内置 public3 解签名得校验和 2,两个相等才信 public1。
第七步:四个连环问——黑客还能钻空子吗?
学完校验证书,我们可能会有一些疑问:
1)黑客能不能只改证书里的公钥?
答:改了立刻露馅。 因为黑客一旦修改了公钥,那么客户端进行证书验证的时候,就会发现自己算出来的校验和和解密签名得到的校验和不同,浏览器会提示风险。
2)那黑客改公钥的同时,把数字签名也改掉呢?
答:还是不能修改,因为数字签名的加密是公证机构的private3完成的,黑客并不知道这个密钥是啥,这个密钥始终在我们公证机构中的。那黑客即使重新计算出了校验和,他也不能计算数字签名。
3)黑客用自己之前的 private2 去「签」一个假证书呢?
答:客户端不买账。 验签时固定用内置的 CA 公钥 public3。用 public3 去解 private2 加密的东西,数学上解不开——锁和钥匙不是一对。
4)我们说客户端要使用公证机构的公钥public3来对数字签名解密,那么我们的黑客能不能对公证机构的公钥public3再来一次中间人攻击,然后把客户端拿到的public3替换成自己的公钥呢?
答:不可以,因为公证机构的公钥public3并不是通过网络传输进行获取的,而是内置在操作系统中的。
第四关小结:
- 第三关卡在「公钥身份未验证」→ 证书 + CA 数字签名补上
- 客户端只信 CA 盖过章的证书,不再盲信网上来的裸公钥
- 验签三步:自算校验和 1,解签得校验和 2,对比;通过才继续传对称密钥
5、HTTPS 如何保证传输安全
那稍微总结一下,我们整个 HTTPS 是如何保证数据传输安全的:
- 引入对称加密,对我们的业务数据进行加密。
- 我们引入非对称加密,对对称密钥进行加密。
- 黑客可以通过中间人攻击替换客户端拿到的公钥。
- 我们的服务器可以通过公证机构申请证书来对公钥进行验证,借助数字签名,客户端就可以确认当前的公钥是合法的。
把完整链路串成一次访问(建立 HTTPS 连接时):
- 客户端向服务器要证书(不是裸公钥);
- 用内置 CA 公钥验证书 → 通过则信任证书里的公钥;
- 客户端生成随机对称密钥,用证书中的公钥加密,发给服务器;
- 服务器用私钥解开,拿到对称密钥;
- 之后 HTTP 请求/响应正文,双方都用这把对称密钥加密传输。
6、Fiddler 与 HTTPS
这里边我们就可以解决以前 Fiddler 的那个问题。我们在开启 HTTPS 选项的时候,首次开启就会提示安装证书,此时安装的就是 Fiddler 的证书——本来你的系统中是没有 Fiddler 的证书的,而且 Fiddler 不是公证机构。而我们的 HTTPS 是加密传输的,那么 Fiddler 是怎么进行解密、还原处理原始数据的呢?
其实 Fiddler 抓取 HTTPS 就是靠中间人攻击实现的,只不过他这个攻击是你许可下的中间人攻击。
具体怎么干的? 你勾选解密 HTTPS 并安装 Fiddler 根证书之后:
- 浏览器访问
https://某网站时,Fiddler 拦在中间; - Fiddler 自己当场伪造一张「某网站」的证书,用 Fiddler 自己的 CA 签名;
- 浏览器验签时,发现 Fiddler CA 在你系统的信任列表里 → 不报错;
- 浏览器把对称密钥用 Fiddler 伪造证书里的公钥加密发给 Fiddler,Fiddler 解密拿到 key;
- Fiddler 再和真正的服务器建立 HTTPS,用真证书完成握手,在中间解密、查看、再加密转发。
如下图所示:

浏览器拿到 Fiddler 的证书之后,为什么没报错?因为你之前已经在系统中安装了 Fiddler 提供的证书,对系统来说,Fiddler 就像公证机构一样的,其中就包含了 Fiddler 的公钥,所以此时拿到 Fiddler 的证书,就可以使用刚才安装好的 Fiddler 公钥进行数字签名的解密和后续的校验。【其实就是你把你的工作委托给 Fiddler 来做了,你安装他的这个相当于是给他授权】
那为啥黑客不能替换这个证书呢?因为在你的系统中没有安装黑客的公钥,此时你的系统就没法把黑客使的证书视为是合法的情况。
7、SSL / TLS 与 HTTPS
上述这一系列机制有一个专门的名字来描述,叫做 SSL(Secure Sockets Layer,安全套接字层);现在主流实现是 TLS(Transport Layer Security,传输层安全),可以看作 SSL 的升级版。习惯上仍说「SSL 层」,多数时候指的就是 TLS。
HTTPS = HTTP + SSL(老版)/ TLS(新版)
和分层的关系: 严格说,TLS 工作在 TCP 之上、HTTP 之下——先由 TCP 保证数据可靠送达,再由 TLS 加密通道,最上层才跑 HTTP 报文。我们常说「HTTPS 是安全的 HTTP」,并不是 HTTP 协议本身变了,而是底下多铺了一层加密隧道。默认端口 443(HTTP 是 80)。
四、写在最后
本篇把 HTTPS 讲完了:对称加密传业务数据、非对称加密传密钥、中间人攻击、证书与数字签名验真,以及 Fiddler 抓包为何能解密——本质是你主动安装了它的证书。
回顾 【网络原理系列】 这一路,我们从协议分层入手,往下走了 TCP/UDP 怎么传、怎么保证可靠和高性能,IP 怎么寻址、NAT 怎么工作;往上则用 Fiddler 抓包,把 HTTP 怎么通信、HTTPS 怎么加密 串了起来。把这些连起来看,浏览器访问网站、APP 调接口时,数据从应用层到传输层、再到网络层,大致是怎么一步步传出去的,心里就有数了。
系列到此完结。 感谢你的点赞、收藏、关注和评论区交流。下一个系列我们换主题再见,写法还是一样:多配图、多例子、把原理讲明白。
我们下一个系列见。

1504

被折叠的 条评论
为什么被折叠?



