简介:一份围绕公钥、私钥、数字签名与数字证书展开的密码学基础解析文档,适合刚接触信息安全、Web 开发或准备面试复习的读者,用于厘清单钥与双钥加密的差别、密钥保存风险以及公钥体系的基本信任问题。压缩包内为 1 个 docx 文件,约 310KB,篇幅集中,便于通读和做笔记。内容从单钥加密的密钥保存难题切入,说明双钥加密中公钥与私钥一一对应、可由公钥加密而难以反推私钥等性质,并提到 DES、RSA 两类代表性算法。数字签名部分按 Hash 摘要、私钥加密摘要、公钥验签的链路展开;数字证书部分则借鲍勃与苏珊的通信案例,解释证书中心如何为公钥背书,以及 HTTPS 中服务器证书的校验思路。已有 627 人学习,适合作为密码学概念入门与知识串联的轻量资料。
1. 两把钥匙:公钥可以公开,私钥反而更危险
一把钥匙可以复制给全世界,另一把必须放在机房里——整个公钥体系就建立在这种不对称之上。很多人第一次接触公钥、私钥、数字签名、数字证书,会把它们当成四个并列的名词去死记;实际上它们是同一条链上的四个环节:双钥算法给出两把有数学关系的钥匙,数字签名回答“这段话是谁说的、有没有被改过”,数字证书再回答“那把公开的钥匙到底属于谁”。链条上任何一环松动,前面所有努力瞬间归零——中间人只要换掉一把公钥,签名照样“验得过”,浏览器照样显示小锁。搞懂这条链的接缝在哪,比背四个定义有用得多。
2. 单钥加密:密钥分发为什么会变成一道死结
2.1 同一把钥匙锁门又开门:单钥体系的封闭世界
单钥加密(private key cryptography)的定义朴素到没有解释空间:加密和解密用同一套密钥,同一把钥匙既锁门又开门。历史上人类能想到的加密几乎都是这一种,从早期的转轮式机械密码机到后来的分组密码标准,思路一脉相承——把明文按某种可逆规则打乱,接收方拿着同样的规则倒推回去。可逆性是它的全部,也是它的全部麻烦。
单钥体系的好处极其硬核:快。现代分组密码在通用 CPU 上能跑到每秒几个 GB,配上专用指令集还能再翻几倍;同样的数据量交给非对称算法,速度会掉两三个数量级。这就是为什么直到今天,真正在传输海量数据的仍然是单钥算法,非对称算法只在开头露个脸。工程上有个粗浅但好用的判断:凡是要处理“很多字节”的地方,底下一定是对称运算。
代价也同样硬核:密钥的保存变成了系统里最脆弱的一环。密钥一旦泄漏,密码随即失效,而且失效得非常彻底——攻击者拿到的不是“以后能解密”,而是“以前也能解密”。我见过不只一个项目把密钥写进配置文件、再跟着代码提交进版本库,事后清理一遍 git 历史就当没事了;只要那份仓库被克隆过、被备份过、被同步到某台测试机过,就相当于密钥从来没有保密过。这不是玄学,这是血泪经验。
2.2 n 个人两两通信要存多少把钥匙
单钥体系最要命的地方不在算法强度,而在数量。任意两个人通信就需要一把专属密钥,n 个参与者两两互通,密钥总数是 n(n-1)/2,也就是随人数平方增长。
| 通信方数量 n | 需要的密钥数 n(n-1)/2 | 一把密钥泄漏的影响面 |
|---|---|---|
| 2 | 1 | 1 条链路 |
| 10 | 45 | 9 条链路 |
| 100 | 4950 | 99 条链路 |
| 1000 | 499500 | 999 条链路 |
几十个人的团队还能靠人工分发撑着,上千个节点就是运维灾难:新增一个人要生成 n 把密钥,删掉一个人要作废 n 把密钥,密钥轮换一次整个矩阵都要重算。更麻烦的是“泄漏影响面”——单钥体系没有隔离性,一把密钥对应一条链路,泄漏一把就等于暴露一对通信方的全部历史流量。
为了把平方级压回线性级,工程上常见的做法是引入密钥分发中心(KDC):所有人只跟中心共享一把密钥,两两通信时由中心签发临时会话密钥。密钥总数从 O(n²) 降到 O(n),看着很漂亮,但代价是把中心变成了单点——中心知道所有人的密钥,中心被攻破等于全网被攻破,而且中心必须永远在线。这是一次典型的设计取舍:用“信任集中”换“管理成本”,并没有真正消灭问题。
2.3 密钥泄漏之后:不是一封信沦陷,是全部历史沦陷
单钥加密还有一个隐患:它天然没有前向保密。长期密钥只有一把,今天泄漏了,昨天抓到的流量包今天照样能解。攻击者甚至不需要实时在线,只要把密文存下来,等哪天拿到密钥再回头慢慢解——这种“先存后解”的思路在真实对抗里非常常见。
补救办法是把长期密钥和会话密钥分开:长期密钥只用来做认证,真正的数据用一次性的临时密钥加密,会话结束就丢弃。这样即使长期密钥将来泄漏,也解不开过去的会话。代价是握手变复杂、需要额外的密钥派生步骤,而且临时密钥的随机性必须真随机——随机数发生器熵不足导致不同设备生成相同密钥的事故,在嵌入式设备上并非传说。
这也解释了为什么“加密”这件事很少单独出现:它总要和密钥管理、密钥轮换、随机数质量捆在一起看。算法本身几乎从来不是短板,短板永远在算法周围。
2.4 换钥匙这件事本身就是个悖论
绕回最根本的矛盾:要安全地传一把密钥,得先有一条安全的信道;要建立安全信道,又得先有密钥。这个循环就是单钥体系的死结。你可以把钥匙装进保险箱寄过去,那保险箱的钥匙怎么寄?你可以派人当面交接,那这个人怎么被信任?
所有的土办法——线下交接、拆成两半走不同渠道、用上一把密钥加密下一把密钥——都只是在把死结往后推一环,并没有解开它。只要有“第一把密钥”需要在不安全信道上传递,整个体系就有一个无法用数学封闭的缺口。双钥体系要解决的,正是这个第一性问题,而不是“把算法做得更难破”。
3. 双钥加密:四条性质里,哪一条才是真正的命门
3.1 四条性质逐条拆:一一对应、互不相同、互为逆运算、正向易反向难
双钥加密(public key cryptography)用两套密钥:公钥公开,私钥保密。它的工作原理通常被概括成四条,这四条不是并列关系,每一条承担的安全职责完全不同。
第一条,公钥和私钥一一对应,有一把公钥必然有一把与之对应的、独一无二的私钥,反之亦然。这条保证的是“配对可靠”——你手里那把公钥对应的私钥只有一份,不会出现两个人共享同一个私钥的荒唐局面。
第二条,所有的(公钥,私钥)对互不相同。这条听起来像废话,实际是在要求随机性足够强。如果密钥生成时熵不够,两台设备完全可能生成同一对密钥,那么“私钥只有一份”这个前提就当场破产。工程上这条比第一条更容易翻车,因为它不会报错,只会安静地削弱安全性。
第三条,用公钥可以解开私钥加密的信息,反之亦然。这条是数字签名的全部数学基础——注意它的措辞是“解开”,不是“解密”。公钥能解开的东西,天然不具备保密性,因为公钥是公开的,谁都能解。很多人卡在这里:既然公钥能解,那用私钥加密不就等于没加密?对,就是这样,所以私钥加密从来不是用来保密的。
第四条,同时生成公钥和私钥相对容易,但从公钥推算出私钥非常困难甚至不可能。这条才是整套体系的安全根基,也是唯一一条“不能证明只能相信”的性质——它依赖的是数学难题的计算复杂度,而不是逻辑上的不可能。
把这四条对齐之后会发现:第三条决定了签名怎么实现,第四条决定了安全性从哪来,第一条和第二条决定了工程实现不能偷懒。教材往往把四条并列着写,读的人就容易以为它们同等重要,实际上缺了第四条,前面三条全是空转。
3.2 陷门在哪:大整数分解与模幂的时间差
双钥算法能成立,靠的是一类叫单向陷门函数的东西。单向的意思是正向算容易、反向算极难;陷门的意思是,如果你掌握某个额外信息(私钥),反向也能算得很快。整个体系的安全性,就压在这个“时间差”上。
以最经典的一类实现为例:选取两个大素数 p 和 q,算出模数 n = p × q,公钥是 (n, e),私钥是 (n, d)。正向运算是一次模幂,用平方-乘算法只需要 O(log e) 次模乘,2048 位规模下通常在一到几毫秒;反向运算要求你从 n 反推出 p 和 q,也就是大整数分解,其计算量随位数呈亚指数增长,2048 位规模下已经远超任何现实算力的可行范围。
这里的关键不是“分解做不到”,而是“分解的代价远高于被保护信息的价值,且高于更换密钥的成本”。密码学从来不给绝对保证,它给的是经济学意义上的保证。这也是为什么密钥长度会随时间上调——不是算法变弱了,是算力变便宜了。
另一族陷门建立在离散对数或者椭圆曲线群上,思路一致:正向是群上的幂运算,反向是求离散对数。不同族的区别主要在“同等安全强度下密钥多长”,椭圆曲线用更短的密钥达到同等强度,所以在移动端和嵌入式场景更受青睐。
3.3 二十行代码看清陷门:把双钥体系缩到最小可运行
把参数缩小到玩具级别,陷门的位置会变得非常直观。下面这段只用于理解机制,任何真实场景都不能这么写。
# 教学用最小双钥实现:只为看清"陷门"落在哪一步 # 严禁用于任何真实系统:没有填充、没有编码、素数也太小 def egcd(a, b): # 扩展欧几里得:求 e 在模 phi 下的乘法逆元,也就是私钥指数 d if b == 0: return a, 1, 0 g, x, y = egcd(b, a % b) return g, y, x - (a // b) * y def keygen(p, q, e=65537): n = p * q # 模数,公开 phi = (p - 1) * (q - 1) # 欧拉函数,用完必须销毁,它等价于私钥 g, d, _ = egcd(e, phi) assert g == 1, "e 必须与 phi 互素" d = d % phi # 私钥指数 return (n, e), (n, d) # 公钥 (n, e),私钥 (n, d) def encrypt(m, pub): n, e = pub return pow(m, e, n) # 模幂:正向运算,毫秒级 def decrypt(c, pri): n, d = pri return pow(c, d, n) # 同一个模幂,指数换成私钥keygen里的 p、q 是陷门本身:拿到 p、q 就能算出 phi,进而算出 d;拿不到就只能对着 n 做分解。encrypt和decrypt用的是同一个pow三参数模幂,区别只在指数——这正是“互为逆运算”那条性质的代码形态,加密和解密不是两套算法,是同一个运算的两个方向。
参数 e 一般固定成 65537,因为它二进制里只有两个 1,平方-乘只需要很少几次模乘,验签(用小指数)会明显更快;d 则必然是一个和 n 差不多长的大数,所以签名和解密都更慢。这个不对称在实际系统里很重要:验签是高频操作,签名是低频操作,让小指数落在高频那一侧是常见优化。
phi那一行值得单独盯一下:它不是中间变量,它和私钥等价。任何打印过phi的日志、任何把 p、q 留在内存里的调试代码,都等于泄漏私钥。
3.4 教科书式双钥的三个坑:确定性、可乘性、密钥长度
上面那段代码能跑通,但它暴露的正是“教科书式实现”的三个典型问题。
第一个坑是确定性。同一个明文、同一把公钥,每次加密得到的密文完全一样。这意味着攻击者可以对候选明文做字典攻击——把常见口令逐个加密,跟密文比对,直接反查明文。解决办法是填充:在明文前加入随机字节再做模幂,让相同明文每次得到不同密文。常见的方案是 OAEP 一类的结构。
第二个坑是可乘性。模幂运算满足 E(m₁) × E(m₂) = E(m₁ × m₂),攻击者拿到密文 C,构造 C' = C × E(k),诱导解密方对 C' 解密,就能从结果里反推出原文。这叫选择密文攻击,也是必须加填充的原因之一——填充破坏了这种代数结构。
第三个坑是密钥长度与用途混用。长度不足会被算力追平,而用途混用(同一对密钥既签名又加密)会让一种场景下的弱点渗透到另一种场景。成熟的实现会给签名和加密使用不同的填充方案(签名侧用 PSS 一类结构),并在证书里用用途字段把两者隔开。这条在后面的排查章节还会再出现一次。
4. 数字签名:为什么要先哈希再“用私钥加密”
4.1 签名不是加密:方向相反的同一个数学动作
教材里最常见的说法是“用私钥对摘要加密,生成数字签名”。这句话能帮人快速记住流程,但严格来说它是错的,而且这个错会引出一连串误用。
签名和加密用的是同一个模幂运算的相反方向,但目标完全不同:加密是为了保密,目标是“只有接收方能看”;签名是为了不可否认,目标是“任何人都能验,但只有持有者能生成”。所以签名的产物必须人人可验——用公钥就能解开。如果你把“私钥加密”当成保密手段,把敏感数据用私钥处理一遍发出去,那等于把内容写在明信片上寄出,因为公钥是公开的,谁都能打开。
常见的实现思路是:签名运算输入的不是裸摘要,而是一个带类型标识的结构(把摘要算法标识和摘要值一起编码),再用私钥做模幂,最后还要加填充。类型标识这一层不是形式主义,它防止的是“把签名当成密文用”的跨用途攻击——如果签名的输入空间和加密的输入空间重叠,攻击者就能诱导一方对另一方的数据做运算。
4.2 哈希在这里干的三件事:压缩、定长、抗碰撞
为什么不直接对整个消息做私钥运算?三个原因,缺一不可。
第一是成本。非对称运算的开销跟着模数长度走,而且每次运算都是几百上千次大数模乘;一段几十兆的文件直接做私钥运算,慢到不可接受。哈希把任意长度的消息压成固定长度(SHA-256 输出 32 字节),签名运算的开销就与消息大小彻底解耦。
第二是定长带来的工程便利。签名值长度固定,意味着它可以塞进固定大小的头部字段、可以独立存储、可以跟原文分离传输。真实的签名往往附带算法标识、证书引用,这些都要求长度可预测。
第三是抗碰撞,这也是安全性真正依赖的那一条。哈希必须满足:给定摘要无法反推原文(抗原像)、给定原文无法找到另一个同摘要的原文(抗第二原像)、无法构造两条摘要相同的不同消息(抗碰撞)。前两条保证“摘要不泄漏内容”,第三条保证“改一个字摘要必变”——这就是雪崩效应,输入翻一个比特,输出大约一半比特翻转。
这里有个容易被忽略的细节:签名保护的是摘要,不是消息本身。所以“摘要→消息”的绑定关系必须由协议自己保证。如果只签了一个 ID,然后用这个 ID 去数据库里取内容,那数据库被改就等于签名失效。早期被攻破的哈希算法(MD5、SHA-1)之所以致命,正是因为碰撞可以让攻击者构造两条内容不同、摘要相同的消息,签了其中一条等于同时签了另一条。
4.3 一次完整的签名与验签:代码和它的时序
签名流程拆开只有四步:发信方对原文做哈希得到摘要;用私钥对摘要做签名运算;把签名附在原文后面一起发出;收信方用发信方公钥解开签名得到摘要,再对收到的原文做一次哈希,两个摘要比对。
import hashlib def sign(message: bytes, pri) -> int: n, d = pri digest = hashlib.sha256(message).digest() # 固定 32 字节摘要 h = int.from_bytes(digest, "big") % n # 摘要转整数,必须小于模数 return pow(h, d, n) # 私钥运算:只有持有者能做 def verify(message: bytes, sig: int, pub) -> bool: n, e = pub digest = hashlib.sha256(message).digest() h = int.from_bytes(digest, "big") % n return pow(sig, e, n) == h # 公钥运算:任何人都能验sign里的% n不能省:摘要被当成一个大整数参与模幂,如果它大于等于模数,映射关系就不再是一一对应的,验签会随机失败。真实方案里这一步由填充结构处理,不会直接对裸摘要取模,但“摘要必须落进模数空间”这个约束是一样的。
verify只做了一次模幂和一次比较,代价很低,所以验签可以放在网关、在边缘节点、在每一条请求上重复执行。这个成本结构决定了整个体系的部署形态:签名侧集中、验签侧分散。
还有一个时序上的细节值得注意:验签必须“先验签、后处理”。如果代码先解析了报文、执行了业务逻辑,最后才补一句验签,那攻击者只要构造一条验签必然失败的报文,就能让业务逻辑跑完。这不是理论问题,是真实项目里反复出现的顺序错误。
4.4 摘要比对到底比的是什么:完整性不等于真实性
两个摘要一致,能得出的结论只有两个:消息在传输过程中没有被修改(完整性),以及这条签名确实由持有对应私钥的人生成(不可否认性)。
但请注意,“持有对应私钥的人”不等于“你以为的那个人”。验签用的是“某把公钥”,而这把公钥是不是对方的,验签算法本身完全不关心。如果攻击者把自己那把公钥塞进你的通讯录,他用自己的私钥签名,你用他的公钥验签,全过程没有任何一步会报错——摘要一致、签名有效、流程完美,只是你验的是一个陌生人的签名。
这就是数字签名解决不了的那个缺口:它证明“这条消息和这把公钥是绑定的”,但不证明“这把公钥属于谁”。补上这个缺口的,是下一层——数字证书。
5. 证书链排查:一把陌生的公钥凭什么被信任
5.1 中间人换钥匙:非对称加密解决不了的那个缺口
设想 A 要给 B 发一封加密信。A 用 B 的公钥加密,只有 B 的私钥能解,这一步数学上无懈可击。现在中间人 C 想办法接触到了 B 的机器,把 B 通讯录里那把公钥换成了自己的公钥;A 完全不知情,仍然以为手里那把是 B 的。A 用 C 的公钥加密发出,C 用自己的私钥解开,读完再用 B 的公钥重新加密转发给 B。B 收到的是正常内容,A 发的是正常内容,全程没有一次解密失败。
这个场景说明:非对称加密解决的是“密钥不用在不安全信道上传输”,它没有解决“这把公钥归谁”。只要公钥的归属没有被验证,前面所有的数学强度都是白搭。任何建立在“公钥即身份”这个假设上的系统,都会在这个环节遇到严重问题。
5.2 数字证书就是一个被签过名的结构体
补缺口的方法很直接:找一个双方都信任的第三方,让它给公钥签字。这个第三方就是证书中心(CA),它用自己的私钥,对“公钥 + 身份信息 + 有效期”这一整块数据做哈希后签名,产出的东西就是数字证书。
把证书拆开看,它不是一个神秘对象,而是一个结构化的数据块,主要字段大致如下:
| 字段 | 含义 | 校验失败会怎样 |
|---|---|---|
| 版本 / 序列号 | 证书格式版本、CA 内唯一编号 | 影响吊销查询,一般不直接报错 |
| 签发者 | 签发这张证书的 CA 名称 | 链匹配失败,找不到上级 |
| 有效期 | 生效时间与失效时间 | 未生效或已过期,直接拒绝 |
| 主体 | 证书持有者的身份信息 | 与预期的对端身份不符 |
| 主体公钥信息 | 被签名的公钥本身,含算法参数 | 后续密钥交换无法进行 |
| 扩展项(备用域名) | 这张证书实际覆盖哪些域名 | 域名不匹配,浏览器告警 |
| 扩展项(用途) | 这张证书允许用来做什么 | 用途越界,被策略拒绝 |
| 签名算法 / 签名值 | CA 用的哈希算法与签名结果 | 签名验不过,链断裂 |
关键点在于:CA 签名覆盖的是“待签名部分”,也就是除签名值之外的全部字段。这意味着证书里任何一个字段被改动,签名都会失效,攻击者没法只改域名不改签名。证书把“公钥”和“身份”用第三方签名绑成了一个不可分割的整体,这就是它比裸公钥强的地方。
5.3 信任链怎么一层层走到根
证书很少是平铺的,而是一条链:服务器持有的是叶子证书,由某个中间 CA 签发;中间 CA 的证书又由上级 CA 签发;一路往上,最后落到一个自签名的根证书。根证书是预先安装在操作系统或浏览器信任库里的,它是整条链的终点,也是唯一不能靠数学证明的一环。
验证的顺序通常是自下而上:用签发者的公钥验证下一级的签名,一级一级往上追,直到追到一个受信任的根。追的过程中还要并行做几件事——检查每一级的有效期、检查叶子证书的域名是否覆盖当前访问的地址、检查用途字段是否允许当前场景、检查算法强度是否达到策略要求(早就被淘汰的摘要算法会被直接拒绝)、查询是否已被吊销(吊销列表或者在线状态查询,近几年更流行的做法是直接把有效期压到很短,用“频繁换证”替代“吊销查询”)。
任何一步失败,链就断了。而链断了的表现形式非常不统一:命令行工具可能报“找不到本地签发者”,浏览器可能只给一个模糊的证书错误,某些客户端干脆连接重置。排查这类问题的第一步永远是把整条链打印出来,看看到底缺了哪一级。
5.4 HTTPS 握手里证书到底用在哪一步
很多资料在讲 HTTPS 时会说“服务器用自己的私钥加密网页,连同证书一起发给客户端”。这个描述作为教学简化可以理解,但照着它去理解真实协议会走偏。
真实流程大致是这样:客户端先发一个请求,带上自己支持的加密套件和一个随机数;服务器回应,附上自己的证书链;客户端验证证书链,确认这把公钥确实属于正在访问的域名;双方通过一次密钥协商(常见的是临时椭圆曲线密钥交换)各自算出同一个会话密钥,这个过程中服务器要用私钥对握手参数签名,证明自己确实持有私钥;握手完成后,所有应用数据都用会话密钥做对称加密传输。
两个关键差异:其一,私钥在握手阶段只用来签名,不用来加密数据;其二,真正传输网页内容的是对称加密,非对称算法只负责认证和协商。原因很实在——非对称运算慢两三个数量级,而且一次能处理的数据长度受模数限制,拿它加密几十 KB 的网页既不现实也没必要;更重要的是,私钥加密后的内容任何人都能用公钥解开,根本不提供保密性。
这套“非对称认证 + 对称传输”的组合,是整个工程界最成功的一次分工:用慢算法解决“信任谁”的问题,用快算法解决“怎么传”的问题。
5.5 五条常见踩坑记录
踩坑一:把“私钥加密”当成保密手段。现象是数据发出去之后,任何拿到公钥的人都能还原内容;原因是公私钥都可公开获取,私钥运算的产物天生不具备保密性;解决办法是把方向摆正——要保密就用对方的公钥加密,要证明身份就用自己的私钥签名,两者不能互换。
踩坑二:证书链只配了叶子证书。现象是浏览器访问正常,但命令行请求、移动端 SDK、某些语言运行时统一报“无法获取本地签发者”;原因是中间证书没有随服务端一起下发,浏览器有补链机制而其他客户端没有;解决办法是把叶子证书和全部中间证书打包成完整链一起配置,根证书不要下发(对端本来就有)。
踩坑三:只校验签名,不校验域名、有效期和用途。现象是自签证书、过期证书、给别人域名签的证书全都能“验签通过”,测试环境一切正常,上线后中间人随便伪造;原因是把“验签成功”当成了“身份可信”;解决办法是固定一套完整校验顺序——先验链、再验有效期、再验域名、再验用途,任何一步失败立即终止,同时保证机器时钟同步,否则有效期判断会莫名其妙地失败。
踩坑四:精简镜像里缺信任库。现象是容器里所有外部 HTTPS 请求全部失败,报证书校验错误,但在宿主机上同样的命令正常;原因是基础镜像为了减小体积没有安装根证书包,信任库文件不存在;解决办法是在镜像构建阶段把根证书装进去,或者显式指定一个可信的证书包路径,不要图省事直接关掉校验。
踩坑五:签名覆盖的字段不完整。现象是报文的主体被签了名,但请求头、时间戳、随机数没被覆盖,攻击者可以重放旧请求或篡改头部;原因是签名只针对“看起来重要”的字段,忽略协议的整体语义,JSON 序列化顺序不一致也会导致同样的报文算出不同摘要;解决办法是把参与业务判断的全部字段纳入签名范围,使用确定性的序列化方式,并在签名内容里带上时间戳和一次性随机数来抵抗重放。
6. 设计取舍:这套体系为什么宁可慢、宁可多一层
把整条链走完,会发现每一处设计都在做同一件事:把“绝对安全”换成“代价可接受的安全”。
| 维度 | 单钥加密 | 双钥加密 |
|---|---|---|
| 运算速度 | 每秒 GB 级 | 每秒千次量级 |
| 密钥分发 | 需要安全信道 | 公钥可公开传递 |
| 身份绑定 | 隐含在密钥里 | 需要证书另行绑定 |
| 适用位置 | 数据本体 | 认证与密钥协商 |
这张表解释了为什么真实系统一定是混合的:非对称算法负责在最开头把“你是谁”这件事说清楚,剩下的活儿全交给对称算法。多一层带来的成本是握手多几个往返、多几次签名验证,换来的是密钥不需要提前分发、不需要在两两之间维护平方级的密钥矩阵。这笔账在任何规模的系统里都划得来。
另一个取舍是证书有效期。为什么不干脆签一张永久有效的证书?因为吊销太难了——一旦私钥泄漏,你得指望全世界所有客户端都及时查到吊销状态,而现实里缓存、离线、实现差异会让吊销形同虚设。于是业界选择了一个更笨但更可靠的办法:把有效期压短,让证书自己过期。多一层运维成本,换掉一个不可靠的机制。
最后一个取舍是信任锚。整条链的顶端是一个预置在系统里的根证书,它没法用数学证明,只能靠预置这个动作本身。这是整个体系里唯一“社会性”的部分,也是它最脆弱的接缝——但它同时也是最务实的选择,因为再往上追,就追到“人怎么信任人”这个无解的问题了。
我自己的习惯是:每次排查任何跟证书、签名相关的问题,都强制走一遍完整链路——先看链是否完整,再看有效期和域名,最后才看签名本身,因为绝大多数“签名验不过”实际上是链断了或者时钟偏了。从那以后我把这套顺序写进排查清单,再没在这上面浪费过一个下午。
这份资料把公钥、私钥、签名、证书这四块串成了一条完整的线,遇到具体协议时翻回去对一遍,比临时搜零散答案快得多。希望帮到你。
本文还有配套的精品资源,点击获取