文中图片因为飞书的原因呈现很模糊,如果有更好的阅读体验感请移步:
https://bcn2i67dv33k.feishu.cn/wiki/K0q3wZ00ziouduk8VsCcrrLanHh?from=from_copylink
本文图片是我自己手画的,如果存在不正确,不严谨的地方还请各位大佬指出:草稿图放在文章最后了
HTTPS 是如何确保安全的
想要了解HTTPS为什么是安全的,我们需要先把前提知识 "加密" 先学习清楚
引入加密算法
加密这个概念欧洲早在 公元前就已经提出来了,并探索实现了不同的 上古加密算法
比如凯撒加密:
|-----------------------------------------------------------------------------------------------------|
| 如果需要保密,信中便用暗号,也即是改变字母顺序,使局外人无法组成一个单词。如果想要读懂和理解它们的意思,得用第4个字母置换第一个字母,即以D代A,余此类推. ------苏维托尼乌斯,罗马十二帝王传 |
密文:Rqh Folfn Wulsoh, Pdnh d Iruwxqh
明文:One Click Triple, Make a Fortune
这便是凯撒加密(偏移量3)的效果
如果你说这种太简单了,懂凯撒密码的都能破解:也存在一种也比较简单但是实用的算法,就是借助"密钥"
比如你想要上课给女朋友写一张小纸条来表示 I LOVE YOU 这种肉麻的话,但是又害怕老师或者同学抓到自己在这偷摸的谈情说爱,就可以采用这个小技巧
首先你需要准备两张纸,在第一张纸上面写上:
The lecture today was kind of boring I feel LOVE a bit sleepy YOU know, want to grab lunch later?
这种看似闲聊无味的话,
然后再在第二张纸上 有意的凿出一些小洞,前提是将两张纸重合后,在第二张纸中你需要传递真实信息的地方【I LOVE YOU】凿出小洞,然后将两张纸重叠就可以"过滤"掉冗余的信息,得到真正需要传递的信息了
对于上述例子
明文便是:唠家常的那些话
密文是:I LOVE YOU
密钥是:第二张纸,并与第一张重合起来
这其实技术一种早期的"密钥式"加密思想:
信息(明文) + 密钥(解密规则) = 真正可读的内容。
对加密有了一定的了解后:
我们来看一下:对称加密和非对称加密
- 对称加密:存在一个密钥:既可以加密也可以解密
- 非对称加密:存在两个密钥k1和k2,可以通过k1进行加密,k2进行解密,也可以通过k2进行加密,k1进行解密
对于非对称加密需要注意的是:需要把两把密钥中的一个公开出去成为"公钥",一把密钥保存起来成为"私钥"
加密的底层逻辑:毋庸置疑一定和数学相关且一定是来源于数论的知识,在这里我们不讨论加密的证明,只讨论加密的使用,因为在计算机开发中,存在相当完备的库供我们使用
引入对称加密:
客户端或者服务器是生成密钥,然后让对方也持有这个密钥
接下来,客户端发送给服务器的数据[HTTP header,body],就使用这个密钥加密,服务器就使用此密钥解密
这样做并不能保证安全性
一个服务器,并非只服务于一个客户端,而是要给很多客户端提供服务的,这么多客户端使用的是同一个密钥,还是不同的密钥呢?
显然是不同的密钥,绝不能是同一个密钥,不然黑客随便创建一个客户端与服务器进行通信便能拿到那一份统一的密钥
- 如果密钥是由客户端这边生成
在通信过程中需要将密钥交给服务器二者之间才能完成加密通信吧,这里不妨令客户端生成一个随机的密钥,然后通过网络发送给服务器,可能会出现以下情况:

有的人会想到,那我再加一个777密钥对888密钥进行再加密不就好了,如果这么做仍然需要明文传递密钥777,
那我再加一个666密钥对777密钥进行加密,但问题在于:总有一个密钥需要明文传输 ,否则通信双方永远无法同步密钥。因此,这种套娃式方案无法从根本上解决密钥泄露问题。
- 如果密钥是由服务器这边生成
大致逻辑与上述描述类似,

可以看到无论是密钥由客户端生成还是由服务器生成都不能保证密钥是否会被窃取,也就不能保证网络通信的安全性,,所以由此可以暂时做出结论,在网络通讯的初始连接阶段并不适合通过对称加密进行通信传输
引入非对称加密
为了解决"密钥交换的安全性"问题,引入了非对称加密( Asymmetric Encryption ):
公钥( public key ) 和 私钥( private key )都是由服务器生成的;
公钥是可以公开给任何人的
但是私钥只能由服务器严格保管,绝不能泄露
客户端通过使用公钥对明文加密得到密文
只有拥有私钥的服务器才能对密文解密得到数据
这样,即使中间人截获公钥和加密后的密钥,也无法破解内容。
????? 客户端为什么能持有公钥呢?因为上面提到的公钥是 " 公开 " 的,不怕别人知道。

大胆想象一下:黑客能否黑进服务器拿到私钥呢?
当然是可能的:在零几年的时候有个特别活泼的社区叫乌云网:里面聚集了大量的网络安全学习者,他们的平时任务就是黑服务器找漏洞,当时很多互联网厂商都瑟瑟发抖生怕自家的服务器bug被挖出来造成大量的经济损失,至于他们采用的手段我们不研究,但是可以得出并不存在完全安全的服务,任何服务器都有可能会被攻破
引入中间人攻击
上述的通信模型存在一个致命的风险,黑客可以凭借这个致命的风险进行攻击
在谈这个风险前,我们先通过一个电影去预习一下这个知识:

这个电影充分体现了人性,我们采用其中一个情节,并作出一定的简化来引入上述提到的风险问题:
孙红雷在这部剧中的角色是一个 缉毒警察
古天乐带着孙红雷去和 A 进行交易,前提是让孙红雷快速学习 伪装为 B 这个角色,也就是相当于伪装的B孙红雷与A进行交易
然后古天乐又带着孙红雷去和B进行交易,前提是让孙红雷快速学习 伪装 A 这个角色,也就是相当于伪装的A孙红雷又与B进行交易
电影《毒战》里的背叛、伪装和信息被利用,像极了中间人攻击:当网络中间节点被控制或身份无法被可靠验证时,通讯双方的"秘密"就会被截取与滥用;
我们上面提到的例子:客户端持有公钥,是事先内置的场景,比如你写一个小程序,只和你自己控制的服务器通信;程序里写死(hardcode)了服务器的公钥。
如果放在互联网中就是错误的了,
如果直接从别的地方拿一份"公开公钥",那就无法确认是不是目标主机的,比如我获得这个公钥是谷歌的,那就无法与百度进行正常的通信
所以我们下面的例子是(在强调一遍公钥是公开的,不怕任何人知道 ):


- 客户端请求服务器的公钥:
- 客户端向服务器发送请求,询问"咱们的公钥是什么?"但黑客拦截了这个请求。
- 客户端在收到公钥时,无法验证公钥的合法性(例如,没有数字证书或CA验证),只能被动接收。
- 黑客冒充服务器并提供自己的公钥:
- 黑客将自己的公钥(pub_hacker)发送给客户端,假装是服务器的公钥。
- 客户端相信这是服务器的公钥,于是使用pub_hacker加密自己生成的对称密钥(假设为"888"),并将密文发送给服务器。
- 黑客解密对称密钥:
- 黑客拦截客户端发送的密文,并使用自己的私钥(prl_hacker)解密,成功获取对称密钥"888"。现在黑客知道了这个密钥。
- 服务器请求公钥(可能被黑客拦截):
- 服务器也可能发送自己的公钥给客户端,但黑客可能拦截了这个响应。文本中多次出现"服务器 咱们的公钥是什么",表明通信可能被扰乱。
- 最终,服务器将自己的真实公钥(pub_server)发送出去,但黑客可能截获它。
- 黑客重新加密对称密钥并转发给服务器:
- 黑客使用服务器的真实公钥(pub_server)重新加密对称密钥"888",然后将密文发送给服务器。
- 服务器使用自己的私钥(prl_server)解密,成功获取对称密钥"888"。服务器以为这是客户端直接发送的,但实际上是黑客转发的。
- 结果:
- 现在,客户端和服务器都拥有相同的对称密钥"888",并开始使用它进行加密通信。
- 但黑客也拥有这个密钥,因此可以轻松解密所有客户端和服务器之间的通信,甚至篡改数据。这就是中间人攻击的成功。
- 第一次伪装:黑客冒充服务器 → 欺骗客户端
对于步骤2,我们知道公钥是公开的,为什么黑客还要装中间人去骗取服务器的公钥呢?主要是为了下一步操作,把自己的pub_hacker发送给客户端,因为自己持有pri_hacker待会可以对客户端发来的加密数据进行解密,因为客户端接收到了自己的pub_hacker公钥嘛。这个是第一次伪装1
- 第二次伪装:黑客冒充客户端 → 欺骗服务器
对于步骤5,黑客最终拿到客户端的加密数据,并进行成功解密后,通过服务器真实的公钥pub_server,冒充了客户端将密文发送给服务器.这个是第二次伪装2
引入证书
邪不压正,我们来想一想如何应对这种中间人攻击的手段,
要想挡住中间人攻击,关键是确认"你在跟谁说话",并且呢明确谈话人的身份信息。
要把中间人打掉,就得做到三件事:
- 第一,保证每一方的身份都能被可靠地证明(也就是建立一个大家都信任的"身份证明体系");
- 第二,把能解密信息的私钥放在安全的地方(比如专门的硬件或受控服务里),别随便放在普通机器或备份里;
- 第三,通信不要一直用同一个长期钥匙,而是频繁换临时钥匙,并做好日志和告警,能及时发现并处理异常。这三点合在一起,能把风险降到最低。
对于低一点的 身份证明体系,我们引入证书这个概念,证书是由第三方的合法公证机构颁发给服务器的

这个证书的流程,需要经过 校验和的计算,校验和的加密,公证机构生成自己的对称密钥 pub_notray / pri_notary(公证), 公证机构通过使用pri_notary对校验和进行加密,最终得到签名
这里的签名本质就是 "加密的校验和",拿着原始数据的每个字节代入数学公式计算,得到一个数值,
原始数据相同校验和算法相同,最终得到的校验和的值一定相同

- 客户端拿着相同的算法计算校验和 得到check1
- 客户端拿着公证机构的公钥对签名进行解密,也得到校验和 check2
- 对比check1 和 check2 是否相等,可以得到证书的有效性
- 如果不相等说明证书是假的,浏览器弹出一个红色页面说明网站存在安全风险
需要注意的是:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 证书里本身就描述了签名使用的算法和所用的哈希函数,客户端验签时按证书里声明的方法来处理: * 证书字段 :X.509 证书包含 signatureAlgorithm 字段(通常是一个 OID),它告诉你签名是用 RSA、ECDSA、Ed25519 等哪种公钥算法,并且通常包含哈希算法(比如 sha256WithRSAEncryption 表示用 SHA‑256 + RSA)。 * 要验证的数据 :对证书本身的"待签名部分"(tbsCertificate)按该哈希算法计算摘要(这就是你说的 check1)。注意不是对整个 PEM 文本做哈希,而是对 tbsCertificate 做哈希。 * 签名格式 :不同算法签名结果的结构不同(RSA 的签名是一个大整数;ECDSA 的签名是一个 (r,s) 对;Ed25519 则是固定格式)。客户端必须知道如何把解出的签名字节按相应算法解码/验算。 简言之:**算法由证书自己声明(**signatureAlgorithm ),客户端按该声明对 tbsCertificate 做哈希并用对应验签方式 。 |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CA**(公证机构)的公钥如何获得(客户端为什么能用公钥解密签名)** 有几种来源和流程,浏览器/客户端通常会组合这些来源来建立链和最终信任根: 服务器在 TLS 握手中发送的证书链 * 在 HTTPS/TLS 握手时,服务器会把自己的叶证书 和通常的中间证书一起发给客户端(称为证书链/chain)。这些中间证书里包含了签发者(Issuer)的公钥和签名算法信息,客户端用这些信息逐层验证到受信任的根。 * 如果服务器没提供某些中间证书,客户端可以尝试通过证书的 AIA(Authority Information Access)字段去下载(HTTP)中间证书。 根证书 / 根 CA 的公钥来自本地信任库( trust store ) * 最终链必须以一个根 CA(self‑signed)结束,该根的公钥/证书通常预置在操作系统或浏览器的受信任根证书存储区(例如 Windows 证书库、macOS Keychain、Linux 的 /etc/ssl/certs 或 NSS/Firefox 的证书库)。 * 浏览器/操作系统通过更新来维护这些受信任根;企业可以手动把自签名根安装到信任库以便内网使用。 证书链验证流程(客户端做的) * 客户端从服务器收到叶证书 + 中间证书,依次用上一级证书的公钥去验证下一级的签名(即用 Issuer 的公钥验证 Subject 的签名),直到达到某个受信任的根证书------根证书的公钥来自本地信任库。 * 如果某一级签名验证失败、链不完整或链末端不在信任库内,验证不通过,浏览器会报警(比如红色警告页)。 证书撤销检查( CRL/OCSP/Stapling ) * 即便签名验证通过,也要检查证书是否已撤销。客户端通常会使用 OCSP、CRL 或利用服务器的 OCSP‑stapling 来检查撤销状态。撤销状态失败也会导致警告。 |
以上两个引用说的通俗一点就是:
对于计算校验和的算法都是公开的且附带在证书中的随时可用
对于拿着公证机构的公钥进行解密的,客户端咋得到的公钥: 、
你的电脑**[** 正版操作系统 **]**和浏览器出厂时就自带了一堆官方认证机构的"公章"样本(根证书),当网站出示它的数字证书时,浏览器就用自带的"公章"去核对证书上的"签名"是否匹配,匹配就说明证书是真的。
