密码学 09:模运算、群与困难问题需要多少数学
浏览器和 HTTPS 服务端没在线上发一把共同密钥,却能算出一把;钱包用私钥生成签名,节点只拿公钥就能验证。这些关系看着像“颠倒运算方向”,但若先读完一本数论教材才能解释,读者很可能忘了它们解决的原始问题:不安全链路上怎样形成共同秘密,或怎样让不知道私钥的人验证授权。这篇只补能看懂 DH、RSA、椭圆曲线责任分工的数学,不把小整数游戏说成生产密码学。
在本章所有例子中,数字刻意小得能手算;所谓“私有指数”也写在公开文章里。没有任何保密性承诺。真实参数、消息编码、身份验证和侧信道还要另行处理。
模运算是按一个固定范围计算
29 % 23 == 6:每次计算只保留除以 23 后的余数。Python 可直接执行 pow(5, 6, 23),意思是 5 的 6 次方再对 23 取余,不必先真的造一个巨大整数。许多密码学结构需要在这样的有限集合中计算。这里最重要的不是记定义,而是画出什么信息公开,什么输入暂不公开。
1 | |
模数规定结果落在哪个有限集合;群在这里指具备运算与逆运算等结构的集合;困难问题指给定规定的公开输入,在资源限制下难以找出相应私有量。困难性属于选定算法、群、参数及对手计算能力的组合,不属于“用了 pow()”这一行代码。真实协议还会检查输入是否处于正确的组或曲线,避免恶意构造的点和可预测的小群;课堂的数字全不满足安全部署要求。
DH:彼此能算同一个结果,旁人没有同样的私有输入
教学例子约定公开 p=23, g=5。甲私下选 a=6,发 A=5^6 mod 23=8;乙私下选 b=15,发 B=5^15 mod 23=19。甲计算 B^a mod 23=2,乙计算 A^b mod 23=2。因为双方等价地组合了同样的两次指数运算,结果一致,线上交换的是 8 与 19 而不是 6、15 或直接发送结果 2。
1 | |
旁观者确实能看到 p,g,A,B,在这个小到可穷举的例子里,几秒就能找出 a 或 b。真实 DH 的安全性需选合适参数和运算群,且还得由协议防止对手插入自己的 share。相同结果不等于确认对方身份:中间人可分别与甲、乙交换公开值,从而建立两条不同的秘密。10 篇用教学模型给出可失败的中间人反例和真实 X25519 原语,15 再看 TLS 如何绑定身份与握手数据。
RSA:知道模数的分解与不知道不是同一种处境
另一个课堂例子取 p=11、q=13,乘积 n=143。相关指数运算用公开 e=7,与 (p-1)(q-1)=120 的某个模逆元 d=103 配对,因为 7*103 mod 120 == 1。将消息整数 42 按数学原语算 42^7 mod 143=81,再以私有指数算 81^103 mod 143=42。这里的 143 可一眼分解,当然没有安全性。
1 | |
这样的模幂原语不等于“直接用公钥加密任何字节”,更不等于“拿私钥加密就叫数字签名”。真实 RSA 加密需要明确消息编码和填充,例如 OAEP;签名则有不同的编码、哈希及随机盐规则,如 RSA-PSS。证书里 RSA 公钥用于验证握手签名,和旧 TLS 1.2 的 RSA 密钥传输是两种操作;TLS 1.3 的实际握手要按其规定区分。这里手算一个模逆元只是理解公钥与私有量的关系,不提供可用 RSA API。
椭圆曲线:换掉运算对象,不换掉“公开/私有”问题
教学曲线在模 17 的范围内规定 y² = x³ + 2x + 2。代入 (5,1) 时两侧同余,故这是曲线上的点;(5,16) 也满足,因为 16 在模 17 下相当于 -1。代入 (5,2) 则不满足。检查一个坐标在课堂曲线上仅验证方程,不是验证它来自可信通信方,也不是执行真实椭圆曲线密码学。
与上面的普通整数乘法不同,椭圆曲线方案在合适的点集合上定义点加法,重复相加得到“私有标量 × 公有基点 -> 公有点”。ECDH 让双方将各自的私有标量与对端公有点组合,ECDSA/Ed25519 则用各自明确的签名规则验证消息;它们并不是把 RSA 的模幂表达式改个名字。曲线、点编码、标量范围、输入验证与安全假设都有具体规范;真实 X25519 参考 RFC 7748,本章的 p=17 教学曲线不是 Curve25519 或 X25519 实现。
这三个例子可以共用一张阅读卡:哪个公开运算易算?哪个私有量由谁持有?如果有人替换公开输入,会不会让验证对象换人?删掉身份绑定后,DH 双方仍可各得一个秘密,但对端可能是中间人;删掉正确消息编码后,RSA 算数例子仍成立,却不能安全地签现实交易。
小参数实验及边界
examples/cryptography/09_math.py 用 Python 标准库 pow() 和整数余数检查上述三组小参数,并拒绝错误点 (5,2)。不存在生产 RSA、ECDH 或曲线代码。
1 | |
2026-10-06 UTC,在 Python 3.12.3 上命令退出码均为 0,3 个测试通过。观察值为 DH 公开 share 8,19、双方共同结果 2;RSA 公私指数算术 n=143,d=103,对 42 变换得 81 再恢复 42;曲线两点为真、错误点为假。改掉一方指数却不更新对方的公开 share,双方结果会不一致;取错 RSA 逆元,恢复断言会失败。手算正确不是“已经证明离散对数难”“已经实现安全签名”或“已经阻止网络中间人”。
两道带答案的练习
推导题。 甲乙通过 DH 得到共同数字 2,网络中的攻击者能替换甲发出的 share。请分别画出“只有一条甲乙共享链路”和“攻击者各接一端”的两种通信图;算出一个共同数字能否区分这两张图?
可核对答案: 正常图交换 A=8,B=19,双方用各自私有指数得 2;中间人图把两条共享会话分别建在甲—攻击者、攻击者—乙之间,每对都能有自己的共享数字。甲只凭自己算出了数字无法判断对端是不是乙,必须有额外可信身份及整个握手的绑定;本章的小数值不作为攻击成本证据。
变更题。 把 09_math.py 里 RSA 私有指数计算改为硬编码 d=102,再运行脚本和测试。哪个断言应失败?如果把曲线上的 (5,1) 改为 (5,16),是否就验证了 Curve25519?
可核对答案: 7*102 mod 120 != 1,逆元断言首先失败,解密也可能不回到 42;(5,16) 在此课堂曲线上确实有效,是 (5,1) 的相反点,但与 RFC 7748 Curve25519 的具体曲线和安全输入验证无关。别从“方程代入成功”推出公钥可信。
资料与导航
- RFC 7919,TLS 中有限域 DH 参数;RFC 8017,RSA;RFC 7748,Curve25519:本篇仅用作各运算家族的规范边界,不照搬小参数部署。
- Boneh–Shoup 作者教材:必要数学与假设的进一步入口。
- 05 随机数、salt 与 nonce · 08 密码为什么不能直接做 SHA-256;10 完成前不提前链接。





