本节的主要内容身份认证与安全协议
为从"为什么要认证"开始,到"怎么认证",再到"认证协议怎么设计才安全",最后讨论密钥协商和秘密共享
一、认证是干什么的
在网络上,你和一个服务器通信时,双方都需要确认对方的身份------你说你是Alice,我怎么知道你不是Trudy?反过来,Alice也要确认自己连接的是真的Bob服务器,而不是一个钓鱼站点。这个过程就叫认证(Authentication)。
认证的核心问题说白了就一句话:一个实体向另一个实体证明自己的某种声称属性是真实的。这里的"实体"可以是人、主机、进程,甚至是一个俱乐部成员资格。
实际中认证通常包含三个层面的含义:
- 身份认证:验证"你是谁",确认通信方的身份是否属实。
- 数据源认证:验证消息确实来自声称的发送者,且中途没有被篡改,同时还要确保消息不是旧的(即"新鲜性")。
- 密钥建立认证:在完成身份认证的同时,协商出一个会话密钥供后续加密通信使用。
后面我们会看到,一个好的认证协议往往要把这三件事一起解决。
二、口令认证:最古老也最头疼的方法
2.1 口令是怎么认证身份的
认证有三种基本途径,也就是常说的三要素:
- 你知道什么(口令、PIN码)
- 你拥有什么(智能卡、USB Key)
- 你是谁(指纹、虹膜等生物特征)
口令属于第一类,也是目前最普遍的方式。它的基本原理极其简单:用户注册时把口令交给服务器存着,登录时再输入口令,服务器比对。如果一致,就认定是你。
这个模型的问题是显而易见的。
2.2 最直白的攻击方式
网络嗅探。如果口令在网络上明文传输,同一网段的任何人都能看到。早期的很多协议就是这个毛病------把口令直接写在消息里发过去。
重放攻击。就算你把口令哈希之后再传,攻击者虽然不知道原文是什么,但他可以直接把你发的哈希值截获下来,然后原样"重放"给服务器,服务器照样认为是你。因为哈希值每次都是一样的,没有"新鲜度"的概念。
钓鱼 。攻击者搭一个假网站,长得跟真的一样,把用户的口令骗过来。用户面对网页时其实很难准确判断这个网站到底是真是假。更隐蔽的是通用密码问题------你为了方便记忆,在很多网站上用同一个口令,结果一个低安全性网站的数据库被拖了,你在高安全性网站的账户也跟着完蛋。
2.3 一个朴素的防御思路:口令哈希
既然不能把同一个密码原样交给每个网站,那就给每个网站生成一个唯一的版本:
hash(password + "banka.com") → Q7a+0ekEXb
hash(password + "siteb.com") → OzX2+ICiqc
这样,即便某个网站的哈希值泄露了,攻击者拿着一长串哈希也无济于事------换一个网站就完全是另一个值了。这是客户端的一种安全防范策略,事实上现在很多密码管理器就在做类似的事情。
2.4 什么样的口令更安全
直觉上,"123456"显然不安全,"jfIej(43j-EmmL+y"显然更安全。问题在于,人类记忆能力有限,几十上百个账户的情况下,用户要么选简单的、有规律的口令,要么就在不同账户之间重用一个口令。
现实中常见的口令设置策略包括:长度限制、必须包含数字、必须包含大小写、必须包含特殊字符、以及黑名单机制。NIST(美国国家标准与技术研究院)发布的数字认证指南就是用来指导这些策略的,但早期的指南缺乏用户行为的实证数据支撑。
三、口令强度到底怎么评估
3.1 两步分析框架
评估一个口令数据集的安全性,通常分两步:先做特征分析 ,再做可猜测性分析。
特征分析关注的是"人们倾向于怎么设置口令":
- 字符分布:数字、字母、特殊字符各占多少比例。
- 结构模式 :如果记L为字母段、D为数字段、S为特殊字符段,那么
password123的结构就是 L₈D₃,abc!123就是 L₃S₁D₃。统计这些结构的流行程度能够揭示人们设置口令的习惯。 - 键盘模式 :很多人会直接在键盘上划------同行的连续字符(如
asdfgh)、锯齿形(如qawsxd)、蛇形(如zxcfgh)。 - 语义模式:中文拼音、英文单词、生日日期(6位或8位)在哪几个位置出现,这些都是攻击者重点尝试的方向。
通过分析泄露的明文口令库(仅国内就有数亿条明文记录泄露过),可以清晰地看出最流行的口令和最常见的行为模式。
3.2 熵:衡量不确定性的数学工具
口令强度的本质,是攻击者猜出它的难易程度。Shannon在1948年提出信息熵的概念,用来度量一个随机变量的不确定性。
如果一个信源有n种取值 U₁, U₂, ..., Uₙ,对应概率为 p₁, p₂, ..., pₙ,那么Shannon信息熵为:
H(X)=−∑i=1npilog2pi H(X) = -\sum_{i=1}^{n} p_i \log_2 p_i H(X)=−i=1∑npilog2pi
但Shannon熵描述的是"平均"不确定性,对口令场景来说有些不够用。因为攻击者根本不会按均匀分布去猜------他们总是先猜 123456,再猜 password。所以实际中用得更多的是下面几个指标:
-
最小熵 (Min Entropy):只关注概率最大的那个事件,衡量"最坏情况"下的不确定性。
H∞(X)=−log2(p1) H_{\infty}(X) = -\log_2(p_1) H∞(X)=−log2(p1)
这里 p₁ 是概率最大的那个口令出现的概率。如果数据集中
123456占了30%,那么最小熵就由它决定。 -
猜测熵 (Guessing Entropy):假设攻击者按概率从高到低依次猜测,猜到任意一个口令平均需要尝试多少次。
G(X)=∑i=1Npi⋅i G(X) = \sum_{i=1}^{N} p_i \cdot i G(X)=i=1∑Npi⋅i
其中 pᵢ 是按降序排列后的第i个概率。
-
β-success-rate(β成功率):攻击者被限制最多进行β次尝试时,他的平均成功率。
λβ(X)=∑i=1βpi \lambda_{\beta}(X) = \sum_{i=1}^{\beta} p_i λβ(X)=i=1∑βpi
-
α-work-factor(α工作量因子):攻击者想要达到至少α的成功率,每个账户至少需要尝试多少次。
μα(X)=min{j | ∑i=1jpi≥α} \mu_{\alpha}(X) = \min\left\{j \;\middle|\; \sum_{i=1}^{j} p_i \geq \alpha\right\} μα(X)=min{j i=1∑jpi≥α}
举个例子,假设一个数据集里 123456 占30%,iloveyou 占20%,123456789 占10%。那么:
- α = 0.4 时,α-work-factor = 1(只需猜
123456就能覆盖40%的账户) - α = 0.9 时,α-work-factor = 3(猜前三个就能覆盖90%)
这就是为什么一个看似"需要尝试上亿次"的口令空间,在实际中很可能几千次尝试就能拿下相当比例的账户。
3.3 攻击者是怎么批量猜口令的
这里说的攻击叫漫步口令猜测攻击(Trawling Guessing)------攻击者不关心具体攻击谁,只是在允许的猜测次数内尽量多猜出一些账户。
基于PCFG(概率上下文无关文法)的方法,是2009年Weir等人提出的第一个全自动化猜测算法。核心思路是:
- 训练阶段:对泄露口令进行解析,统计每种结构(如 L₄D₃、L₅S₁D₂)出现的频率(结构频率表 Σ₁),以及每种结构中字母段/数字段/特殊字符段的具体内容频率(字段频率表 Σ₂)。
- 猜测集生成阶段:按 Σ₁ 和 Σ₂ 中统计的概率降序生成猜测集。
一个口令 wang123! 会被解析为 L₄(wang)、D₃(123)和 S₁(!),那么它的概率为:
P(wang123!)=P(L4D3S1)×P(L4→wang)×P(D3→123)×P(S1→!) P(\text{wang123!}) = P(L_4D_3S_1) \times P(L_4 \to \text{wang}) \times P(D_3 \to 123) \times P(S_1 \to !) P(wang123!)=P(L4D3S1)×P(L4→wang)×P(D3→123)×P(S1→!)
整个思路本质上是:先确定"结构"有多常见,再确定在那种结构下的"内容"有多常见。
基于Markov模型的方法 (Narayanan & Shmatikov, 2005)则换了一个思路:它假设用户构造口令时是从左往右一个字符接一个字符地输入。在4阶Markov模型下,口令 abc123 的概率是:
Pr(abc123)=Pr(a∣⊢)⋅Pr(b∣a)⋅Pr(c∣ab)⋅Pr(1∣abc)⋅Pr(2∣abc1)⋅Pr(3∣bc12) Pr(\text{abc123}) = Pr(a|\vdash) \cdot Pr(b|a) \cdot Pr(c|ab) \cdot Pr(1|abc) \cdot Pr(2|abc1) \cdot Pr(3|bc12) Pr(abc123)=Pr(a∣⊢)⋅Pr(b∣a)⋅Pr(c∣ab)⋅Pr(1∣abc)⋅Pr(2∣abc1)⋅Pr(3∣bc12)
其中 ⊢ 表示起始符号。训练阶段统计每个子串后面跟每个字符的频数,猜测时按条件概率从高到低生成。
PCFG着眼于结构层面,Markov着眼于字符转移层面,两者的共同点是都从真实泄露数据中学习人类的口令构造习惯。
3.4 口令世界的困境
口令面临一个根本矛盾:可记忆性 vs. 抗猜测性------前者希望口令短、有规律、简单;后者希望口令长、无规律、复杂。这两者是直接对立的,不存在一个完美的折中方案。
目前学界的共识是:
- 保核心:重要账户用强口令,不重要的可以随便一些
- 减负担:同级别账户用类似口令
- 不越级:不同级别账户绝对不重用类似口令
- 如果使用口令管理软件,最好是自己编写的(信任问题)
四、认证协议:从"最低配"到"安全"
前面讨论的是"用什么信息来认证",接下来要关注的是"在网络上怎么交互才能安全地完成认证"------这就是认证协议设计的核心问题。
这里先说明一下符号约定。后面会反复出现这些记号:
{M}_Alice:用Alice的公钥加密消息M[M]_Alice:用Alice的私钥对M签名E(M, K):用对称密钥K加密Mh(...):哈希函数- Alice和Bob分别代表通信双方,Trudy代表攻击者
4.1 最原始的做法:直接传口令
Alice → Bob: "I'm Alice"
Bob → Alice: Prove it
Alice → Bob: My password is "frank"
这个协议有两个致命缺陷:Trudy截获口令后可以直接拿去用(明文传输),而且就算Trudy当时没截获完整口令,她只要把之前偷听到的消息原样重放给Bob,Bob照样会认为她就是Alice。
改进一:做个哈希。
Alice → Bob: "I'm Alice"
Bob → Alice: Prove it
Alice → Bob: h(Alice's password)
这样至少口令本身不会直接暴露。但Trudy依然可以截获 h(password) 然后重放------哈希值每次都是一样的,没有任何"这次"的特征。
4.2 核心机制:挑战-应答(Challenge-Response)
解决问题的关键思想是:每次认证时,服务器先发一个一次性随机数(Nonce),客户端把这个随机数和自己知道的口令或密钥混合在一起做运算,结果返回服务器。服务器也做同样的运算,对比结果。
Alice → Bob: "I'm Alice"
Bob → Alice: Nonce
Alice → Bob: h(Alice's password, Nonce)
解释一下为什么这样就防住了重放:
- Nonce是Bob每次新生成的,用过一次就作废。
- 即便Trudy截获了
h(password, old_nonce),下次Bob发一个新的Nonce,这个旧响应就完全没用了。 - Nonce保证了"新鲜性"------每次的挑战值不同,每次的应答也不同。
Nonce可以用随机数,也可以用时间戳。时间戳的好处是省掉一轮消息交换,但代价是双方时钟需要大致同步,而且时间本身成了一个关键安全参数。
4.3 对称密钥认证
把口令换成共享的对称密钥 K,对Nonce做加密而不是哈希:
Alice → Bob: "I'm Alice"
Bob → Alice: R
Alice → Bob: E(R, K)
Bob用同样的密钥K解密,比对R是否正确。能正确解密,就说明对方确实持有K------因为只有Alice和Bob知道这个密钥。
但注意,这个协议只做到了单向认证:Bob认证了Alice,但Alice并没有认证Bob。Trudy完全可以假装成Bob给Alice发Nonce。
4.4 双向认证(相互认证)及其陷阱
很自然地想到:把上面的过程反过来再做一次就行了。于是有了第一个双向认证的尝试:
Alice → Bob: "I'm Alice", R_A
Bob → Alice: R_B, E(R_A, K)
Alice → Bob: E(R_B, K)
这个协议看起来对等、工整,但实际上存在一个很典型的漏洞------反射攻击(Reflection Attack):
Trudy可以在Bob面前伪装成Alice:
- Trudy 以"Alice"的身份发 R_A 给Bob
- Bob 返回 R_B 和 E(R_A, K)
- Trudy 开启一个新的连接,又以"Alice"的身份发 R_B 给Bob
- Bob 返回 R_C 和 E(R_B, K)
- Trudy 拿着第4步得到的 E(R_B, K) 回到第1个连接中,发给Bob
Bob收到 E(R_B, K) 后验证通过,就以为Trudy是Alice。
这个攻击揭示了一个深刻的经验法则:不要在双向认证中让双方做完全对称的操作。对称意味着攻击者可以利用一方来欺骗另一方。
修正方案很简单------在加密内容中加入身份标识:
Alice → Bob: "I'm Alice", R_A
Bob → Alice: R_B, E("Bob", R_A, K)
Alice → Bob: E("Alice", R_B, K)
只是多加了身份标签,但彻底堵住了反射攻击的路。安全协议设计就是这样,往往一个微小的改动就能产生质的变化。
4.5 公钥认证
利用非对称密码体制,思路类似,但换成公私钥对:
基于公钥加密的认证(首次尝试):
Alice → Bob: "I'm Alice"
Bob → Alice: {R}_Alice
Alice → Bob: R
Bob用Alice的公钥加密R,只有持有私钥的Alice能解密出R。但如果加密和签名用的同一个密钥对,Trudy可以让Alice"帮"自己解密任何东西------这本质上相当于把Alice当成解密预言机。所以加密和签名必须用不同的密钥对。
基于数字签名的认证:
Alice → Bob: "I'm Alice"
Bob → Alice: R
Alice → Bob: [R]_Alice
Alice对R签名,Bob用Alice的公钥验证。同样地,签名密钥对和加密密钥对要分开,否则Trudy可以诱骗Alice对任何东西签名。
4.6 认证的同时建立会话密钥
认证完成后,双方通常还需要一个会话密钥(Session Key),用于后续通信的加密和完整性保护。这个密钥只存在于当前会话中------即便攻击者以后拿到了长期密钥,也没法解密本次会话的记录。这就是**完全正向保密(Perfect Forward Secrecy, PFS)**的思想。
把认证和会话密钥结合在一起的尝试:
对称密钥版本(有问题):
Alice → Bob: "I'm Alice", R
Bob → Alice: {R, K}_Alice
Alice → Bob: {R+1, K}_Bob
这里Bob生成了会话密钥K,但Alice没法认证Bob------Alice的Nonce值R在这里根本起不到认证Bob的作用。
签名版本(有严重漏洞):
Alice → Bob: "I'm Alice", R
Bob → Alice: [R, K]_Bob
Alice → Bob: [R+1, K]_Alice
问题出在哪?签名是公开可验证的!任何人拿到Bob的签名消息 [R, K]_Bob,用Bob的公钥就能解开看到K。密钥直接在签名里暴露了。
先签名后加密(仍然不安全------中间人攻击):
Alice → Bob: "I'm Alice", R
Bob → Alice: { [R, K]_Bob }_Alice
Alice → Bob: { [R+1, K]_Alice }_Bob
Trudy的攻击路径:
- Trudy截获第1条消息,将R转给Bob,但声称自己是"Trudy"
- Bob用Trudy的公钥加密
[R, K]_Bob返回 - Trudy用自己的私钥解密(注意外层是用Trudy公钥加密的),拿到Bob的签名
[R, K]_Bob,从而获得K - Trudy把
[R, K]_Bob用Alice的公钥加密返回给Alice - Alice以为在和Bob通信,拿到的却是Trudy转发的密钥K
先加密后签名(这版是对的):
Alice → Bob: "I'm Alice", R
Bob → Alice: [ {R, K}_Alice ]_Bob
Alice → Bob: [ {R+1, K}_Bob ]_Alice
先加密保证了只有接收方能看到密钥,再签名保证了发送方的身份。这个顺序是关键的:签在外层,别人没法剥离签名偷换内容。
4.7 完全正向保密(PFS)
前面的方案都有一个隐含的假设:如果长期密钥K日后泄露了,之前记录的加密通信就全都能被解密。PFS要解决的就是这个问题------即使长期密钥泄露,过去的会话也不能被解密。
PFS的关键是使用临时密钥(Ephemeral Key)。最经典的实现是对称密钥保护下的临时Diffie-Hellman:
Alice → Bob: E(g^a mod p, K)
Bob → Alice: E(g^b mod p, K)
双方在长期密钥K的保护下交换临时的DH公开值 g^a 和 g^b,会话密钥 K_S = g^{ab} mod p。会话结束后,双方各自丢弃临时指数a和b。此后即使K泄露,攻击者也无法从截获的密文中恢复出 g^{ab}------而DH问题本身的困难性(离散对数)保证了这一点。
这种方案叫做短时Diffie-Hellman(Ephemeral DH),在TLS等实际协议中广泛使用。DH本身容易遭受中间人攻击(因为传输 g^a 时没人知道是Alice还是Trudy发的),但这里用长期密钥K加密传输,相当于在DH外面包了一层认证,同时解决了MiM和PFS两个问题。
4.8 用时间戳替代Nonce
有时候为了减少消息轮数,会用时间戳T替代Nonce。在公钥体系下:
Alice → Bob: "I'm Alice", { [T, K]_Alice }_Bob
Bob → Alice: { [T+1, K]_Bob }_Alice
时间戳的好处是少了一轮交换,但代价是双方时钟必须大致同步(需要容忍一定的时间偏斜),而且时间成了关键安全参数------Trudy如果能在时间窗口内发起攻击,就仍然有机会。
五、Diffie-Hellman密钥协商
5.1 基本思想
前面的协议里,会话密钥要么由某一方生成后传输给另一方,要么由KDC分发。但在某些场景下,我们希望密钥由通信双方共同生成------在协议完成之前,谁也不知道最终密钥长什么样。
Diffie-Hellman协议(1976年)就是为此设计的,也是应用最广泛的密钥协商协议。它允许双方通过公开信道交换一些公开值,然后各自独立计算出相同的共享密钥。
5.2 协议过程
选定一个有限群G(通常是模大素数p的乘法群),以及一个生成元g。
Alice: 选取随机数 a, 计算 g^a mod p, 发送给Bob
Bob: 选取随机数 b, 计算 g^b mod p, 发送给Alice
Alice: K = (g^b)^a = g^{ab} mod p
Bob: K = (g^a)^b = g^{ab} mod p
实例(p = 23, g = 7):
| 步骤 | Alice (a=3) | Bob (b=6) |
|---|---|---|
| 计算并发送 | 7³ mod 23 = 21 | 7⁶ mod 23 = 4 |
| 收到对方值后计算 | 4³ mod 23 = 18 | 21⁶ mod 23 = 18 |
双方都得到 K = 18。
5.3 安全性基础
DH的安全性建立在离散对数问题的困难性上:从 g^a mod p 推导出 a 在计算上是不可行的。攻击者虽然能看到 g^a 和 g^b,但无法高效计算出 g^{ab}。
需要注意的是,裸的DH协议不提供认证------攻击者可以在中间分别和Alice、Bob各自建立一个DH连接(中间人攻击)。所以实际使用中DH总是需要搭配某种认证机制(如数字签名)一起用。
六、口令之外:其他认证方式
6.1 动态口令(基于信任物体)
口令的问题在于它是静态的------一旦被截获就能反复使用。动态口令每次都不一样,即使被截获下次也用不了。
对称密钥方式:
Alice → Bob: "I'm Alice"
Bob → Alice: R(随机挑战值)
Alice: 输入PIN激活令牌 → 令牌计算 h(K, R) → 发给Bob
Bob: 同样计算 h(K, R) → 比对
令牌每分钟或每次操作产生一个不同的口令。服务器也做同样的计算。挑战值R可以是服务器下发的随机数,也可以是时间戳。
非对称密钥方式:秘密值是示证者的私钥,验证者持有公钥。挑战值用公钥加密后由示证者解密返回------能正确解密就等于证明了持有私钥。
6.2 生物特征认证
生物特征是"你是谁"这一因素。常用的包括指纹、虹膜、人脸、声音等。选用生物特征需要满足四个条件:普遍性(人人都有)、唯一性(人人不同)、可测量性、稳定性(不随时间急剧变化)。
指纹识别:采集图像 → 增强处理 → 提取特征点 → 与数据库比对。本质上是统计匹配,不是精确相等。
虹膜扫描 :虹膜的随机发育过程使其具有极高的唯一性,且遗传影响小、终身稳定。扫描后通过二维小波变换生成256字节(2048位)的虹膜码,然后计算两个虹膜码之间的汉明距离:
d(x,y)=不匹配的位数比较的总位数 d(x, y) = \frac{\text{不匹配的位数}}{\text{比较的总位数}} d(x,y)=比较的总位数不匹配的位数
实操中的阈值:相同虹膜的汉明距离约0.08,不同虹膜约0.50。通常设定阈值为0.32------小于这个值就认为匹配。
6.3 错误率权衡
生物特征系统有两种错误:
- 错误接受率(FAR):把冒充者错误地认证为合法用户
- 错误拒绝率(FRR):把合法用户错误地拒绝
这两个是跷跷板------降低一个必然抬高另一个。**等错误率(EER)**是两者相等时的错误率,用来横向比较不同技术的性能。在理论上,虹膜扫描可以达到 10^{-5} 级别的等错误率,但需要极其精准的注册过程。
6.4 多因子认证:1+1>2
双因素认证意味着同时使用两个不同维度的认证因素------比如 ATM卡(你有什么)+ PIN码(你知道什么)。关键要求是:这两个因素之间不能互相推导。
这里有一个看似"双因素"实则挂羊头卖狗肉的例子:动态口令卡需要一个开机密码来激活,看起来"卡"和"密码"是两个因素。但如果这个开机密码是存在卡里的,那么拿到卡的人物理上就能提取出密码------两个因素退化成了一个。
另一个反例:提供照片+口令的方案,照片识别要么依赖集中数据库(难以支持大用户量),要么数据库分散(不安全)。
6.5 各方对比
比较维度:低成本 | 可用性 | 可再生性
| 方案 | 成本 | 可用性 | 可再生性 |
|---|---|---|---|
| 口令 | 是 | 中 | 是 |
| 硬件令牌 | 否 | 低 | 是 |
| 生物特征 | 否 | 高 | 否 |
在可预见的未来,口令认证仍将是最主要的认证方式。不是因为口令最安全,而是因为它成本最低、最容易部署、也最为用户所熟悉。其他方案综合来看都有各自的短板,无法全面替代口令。
七、秘密共享:当秘密不能只交给一个人
7.1 现实需求
设想这些场景:导弹发射需要两人同时转动钥匙;核按钮需要多人共同授权;银行保险库需要若干负责人同时到场才能开启。这些场景的共同需求是:秘密不能由一个人独占,必须分散到多个参与者手中,并且只有当足够数量的参与者同时到场时才能恢复。
这就是秘密分割(Secret Sharing)要解决的核心问题。它的形式化描述叫做(t, n)门限方案:
- 将秘密s分割成n个子秘密(shadow/share)
- 任意t个(及以上)子秘密可以恢复s
- 任意少于t个子秘密得不到s的任何信息
极端情况下,(n, n)门限方案要求所有人都到场,(1, n)则退化为简单的备份。
7.2 Shamir门限方案的核心思想
Shamir在1979年提出的方案是理解整个秘密共享领域的钥匙。它的思路出人意料地简单:用多项式来隐藏秘密。
对于一个(t, n)门限方案:
-
构造一个 t-1 次多项式:
f(x)=a0+a1x+a2x2+⋯+at−1xt−1 f(x) = a_0 + a_1 x + a_2 x^2 + \cdots + a_{t-1} x^{t-1} f(x)=a0+a1x+a2x2+⋯+at−1xt−1
其中 a0=sa_0 = sa0=s(要保护的秘密),其余系数 a1,...,at−1a_1, \dots, a_{t-1}a1,...,at−1 随机选取。所有运算在有限域 GF§ 上,p为一个大素数。
-
任选n个互不相同的 xix_ixi(i = 1, 2, ..., n),计算对应的 yi=f(xi)y_i = f(x_i)yi=f(xi)。每对 (xi,yi)(x_i, y_i)(xi,yi) 就是一个子秘密,分发给一个参与者。
-
恢复时,只要有任意t个点,就能唯一确定这个 t-1 次多项式,从而得到 f(0)=a0=sf(0) = a_0 = sf(0)=a0=s。
这里的关键直觉是:平面上两点确定一条直线(t=2),三点确定一条抛物线(t=3),依此类推------t个点唯一确定一个t-1次多项式。 少于t个点,则有无穷多种可能的多项式,秘密被完全隐藏。
7.3 Lagrange插值:数学上怎么恢复
已知t个点 (xi1,yi1),(xi2,yi2),...,(xit,yit)(x_{i_1}, y_{i_1}), (x_{i_2}, y_{i_2}), \dots, (x_{i_t}, y_{i_t})(xi1,yi1),(xi2,yi2),...,(xit,yit),可以用Lagrange插值公式重构多项式:
f(x)=∑j=1tyij∏m=1m≠jtx−ximxij−xim f(x) = \sum_{j=1}^{t} y_{i_j} \prod_{\substack{m=1 \\ m \neq j}}^{t} \frac{x - x_{i_m}}{x_{i_j} - x_{i_m}} f(x)=j=1∑tyijm=1m=j∏txij−ximx−xim
那么密钥 k=f(0)k = f(0)k=f(0):
k=∑j=1tyij∏m=1m≠jt−ximxij−xim k = \sum_{j=1}^{t} y_{i_j} \prod_{\substack{m=1 \\ m \neq j}}^{t} \frac{-x_{i_m}}{x_{i_j} - x_{i_m}} k=j=1∑tyijm=1m=j∏txij−xim−xim
如果令
bj=∏m=1m≠jt−ximxij−xim b_j = \prod_{\substack{m=1 \\ m \neq j}}^{t} \frac{-x_{i_m}}{x_{i_j} - x_{i_m}} bj=m=1m=j∏txij−xim−xim
则有 k=∑j=1tbj⋅yijk = \sum_{j=1}^{t} b_j \cdot y_{i_j}k=∑j=1tbj⋅yij。因为所有的 xix_ixi 是公开的,每个 bjb_jbj 可以预先算好,恢复密钥时只需要做简单的线性组合。
7.4 完整演算示例
构造一个 (3, 5) 门限方案,秘密 k = 11,素数 p = 19。
分割阶段:
随机选 a1=2a_1 = 2a1=2, a2=7a_2 = 7a2=7,常数项 a0=11a_0 = 11a0=11,得到多项式:
f(x)=(7x2+2x+11) mod 19 f(x) = (7x^2 + 2x + 11) \bmod 19 f(x)=(7x2+2x+11)mod19
计算5个子秘密:
- f(1)=(7+2+11) mod 19=1f(1) = (7 + 2 + 11) \bmod 19 = 1f(1)=(7+2+11)mod19=1
- f(2)=(28+4+11) mod 19=5f(2) = (28 + 4 + 11) \bmod 19 = 5f(2)=(28+4+11)mod19=5
- f(3)=(63+6+11) mod 19=4f(3) = (63 + 6 + 11) \bmod 19 = 4f(3)=(63+6+11)mod19=4
- f(4)=(112+8+11) mod 19=17f(4) = (112 + 8 + 11) \bmod 19 = 17f(4)=(112+8+11)mod19=17
- f(5)=(175+10+11) mod 19=6f(5) = (175 + 10 + 11) \bmod 19 = 6f(5)=(175+10+11)mod19=6
恢复阶段(任取3个,如 f(2)=5, f(3)=4, f(5)=6):
三个子项分别计算:
f(2) 的子项:
5⋅(x−3)(x−5)(2−3)(2−5)=5⋅(x−3)(x−5)3=5⋅13⋅(x−3)(x−5)=65(x−3)(x−5) 5 \cdot \frac{(x-3)(x-5)}{(2-3)(2-5)} = 5 \cdot \frac{(x-3)(x-5)}{3} = 5 \cdot 13 \cdot (x-3)(x-5) = 65(x-3)(x-5) 5⋅(2−3)(2−5)(x−3)(x−5)=5⋅3(x−3)(x−5)=5⋅13⋅(x−3)(x−5)=65(x−3)(x−5)
f(3) 的子项:
4⋅(x−2)(x−5)(3−2)(3−5)=4⋅(x−2)(x−5)−2=4⋅9⋅(x−2)(x−5)=36(x−2)(x−5) 4 \cdot \frac{(x-2)(x-5)}{(3-2)(3-5)} = 4 \cdot \frac{(x-2)(x-5)}{-2} = 4 \cdot 9 \cdot (x-2)(x-5) = 36(x-2)(x-5) 4⋅(3−2)(3−5)(x−2)(x−5)=4⋅−2(x−2)(x−5)=4⋅9⋅(x−2)(x−5)=36(x−2)(x−5)
f(5) 的子项:
6⋅(x−2)(x−3)(5−2)(5−3)=6⋅(x−2)(x−3)6=6⋅16⋅(x−2)(x−3)=96(x−2)(x−3) 6 \cdot \frac{(x-2)(x-3)}{(5-2)(5-3)} = 6 \cdot \frac{(x-2)(x-3)}{6} = 6 \cdot 16 \cdot (x-2)(x-3) = 96(x-2)(x-3) 6⋅(5−2)(5−3)(x−2)(x−3)=6⋅6(x−2)(x−3)=6⋅16⋅(x−2)(x−3)=96(x−2)(x−3)
合并:
f(x)=65(x−3)(x−5)+36(x−2)(x−5)+96(x−2)(x−3) mod 19=(26x2−188x+296) mod 19=7x2+2x+11 \begin{aligned} f(x) &= 65(x-3)(x-5) + 36(x-2)(x-5) + 96(x-2)(x-3) \bmod 19 \\ &= (26x^2 - 188x + 296) \bmod 19 \\ &= 7x^2 + 2x + 11 \end{aligned} f(x)=65(x−3)(x−5)+36(x−2)(x−5)+96(x−2)(x−3)mod19=(26x2−188x+296)mod19=7x2+2x+11
得 f(0)=11=kf(0) = 11 = kf(0)=11=k,恢复成功。
这个演算直观地展示了:只要凑够t个点,多项式就被唯一确定了;如果只有两个点(少于3个),这个二次多项式就是不确定的------秘密信息理论上完全安全。更重要的一点是,即便攻击者拿到了系统的某些内部数据(某个 s_{i+1}),也无法反推出前一个子秘密 s_i。
总结路线
回顾整条主线:
- 认证是网络安全的基础------所有其他安全措施都建立在"知道在和谁通信"之上。
- 口令是最实际的认证方式,但它面临着嗅探、重放、钓鱼、跨站泄露等各种威胁,而且人类记忆能力的限制使得"好用"和"安全"天然对立。
- 口令强度评估让我们能够量化地理解:为什么有些口令看起来复杂但实际不安全,攻击者是如何利用统计规律批量猜解口令的。
- 认证协议设计从最简单的明文传输,一步步演变到挑战-应答、对称/非对称认证、双向认证、会话密钥建立、完全正向保密------每一步都是在对前一版的缺陷进行修补,而每一次修补都可能引入新的攻击面。
- DH密钥协商 和PFS解决了"如何在公开信道上协商密钥"以及"如何使过去会话不受未来密钥泄露影响"这两个根本性问题。
- 生物特征 和多因子认证尝试弥补口令的短板,但各有各的代价。多因子真正的精髓在于因素之间不可互相推导。
- 秘密共享则是从另一个维度思考信任:不是相信一个人,而是相信一群人中的足够多数。Shamir方案用多项式插值这个纯数学工具实现了这个目的,简洁而深刻。