☰
从仿射变换到RSA:密码学中的模运算与可逆性机制解析
2026/10/28 11:00:51 网站建设 项目流程

简介:这份资源是清华大学杨波《现代密码学》第4版的课后习题与参考答案文档,面向高校密码学课程学习者、考研复习者及需要巩固古典密码体制计算方法的读者,帮助解决习题演算无从对照、模运算与矩阵加密步骤易出错的问题。压缩包内共1个docx文件,约1.61MB,按章节组织习题与详细解答,涵盖仿射变换的加解密与模逆元求解、多表代换密码的矩阵分组运算、线性反馈移位寄存器的输出序列与周期分析等典型题型,解答过程保留明文数字化、密文分组和验证还原的完整推导链条。已有5680人学习下载,适合作为课堂作业对照、期末与考研查漏补缺的演算参考,也可用于核对仿射变换与多表代换的手算结果。

1. 仿射变换:为什么一个乘法系数就能让凯撒密码脱胎换骨

凯撒密码把每个字母平移固定位数,密钥空间只有 25 种,暴力枚举连一杯咖啡都喝不完。仿射变换在平移的基础上加了一个乘法系数,形式变成 E(m) ≡ a·m + b (mod 26),密钥从单个数字变成 (a, b) 二元组。看起来只是多了一个参数,但真正有意思的地方在于:这个乘法系数 a 不是随便取的,它必须与模数 26 互素,否则解密变换根本不存在。换句话说,加密函数是否可逆,在选参数的那一刻就已经被数论决定了。这份习题参考答案里,第 1 章用“THE NATIONAL SECURITY AGENCY”的完整加解密过程,把仿射变换从参数选择、模逆元求解到逐字符验证的整条链路走了一遍。搞懂这条链路,后面多表代换、LFSR、DES、RSA 里反复出现的“模运算 + 可逆性”思路就有了落脚点。

2. 仿射变换的可逆性:模逆元为什么是解密的前提

2.1 加密函数什么时候才配拥有解密函数

仿射变换的加密映射是 m ↦ a·m + b (mod 26)。把它看成整数环 Z₂₆ 上的一个线性函数,解密就是解方程 a·m ≡ c − b (mod 26)。这个方程对 m 有唯一解,当且仅当 a 在模 26 下存在乘法逆元,也就是 gcd(a, 26) = 1。26 = 2 × 13,所以 a 不能是偶数,也不能是 13 的倍数。在 0 到 25 里筛一遍,满足条件的 a 只有 12 个:1、3、5、7、9、11、15、17、19、21、23、25。b 可以取 26 个值,于是仿射密码的密钥空间是 12 × 26 = 312。这个数字不大,但比凯撒的 25 已经高出一个量级,而且它的“不可逆参数自动出局”这个约束,是后面所有公钥体制里“密钥必须满足数论条件”的预演。

参考答案里取的 a = 11、b = 23,11 是奇数且不是 13 的倍数,合法。解密时需要 11 的模逆元。11 × 19 = 209 = 8 × 26 + 1,所以 11⁻¹ ≡ 19 (mod 26)。这一步是整个解密的核心,答案里特意注明“求模逆元可以通过欧几里得算法或者直接穷举 1~25”,因为模数只有 26,穷举完全可行;但到了 RSA 那种几百比特的模数,就只能靠扩展欧几里得算法。

2.2 逐字符加密的完整推演

把明文转成数字是第一步。A=0、B=1……Z=25,这是密码学习题里的标准约定。答案给出的明文数字序列是:

# 明文 "THE NATIONAL SECURITY AGENCY" 的数字表示 # 空格不计入,只对 25 个字母做变换 m = [19, 7, 4, 13, 0, 19, 8, 14, 13, 0, 11, 18, 4, 2, 20, 17, 8, 19, 24, 0, 6, 4, 13, 2, 24] a, b, mod = 11, 23, 26 # 加密:c = (a * m + b) mod 26 c = [(a * x + b) % mod for x in m] # c = [24, 22, 15, 10, 23, 24, 7, 21, 10, 23, 14, 13, # 15, 19, 9, 2, 7, 24, 1, 23, 11, 15, 10, 19, 1] # 解密:m = a_inv * (c - b) mod 26,其中 a_inv = 19 a_inv = 19 m_dec = [(a_inv * (y - b)) % mod for y in c] # m_dec 与原始 m 完全一致

代码里(a * x + b) % mod这一行就是加密变换的全部。Python 的%对负数会返回非负余数,所以(y - b)为负时不需要额外处理,这一点在 C 或 Java 里要小心,得手动加模数再取余。解密那行a_inv * (y - b) % mod里,a_inv 必须是 a 在模 26 下的逆元,答案用 19 是因为 11 × 19 ≡ 1 (mod 26)。如果换成 a = 9,逆元是 3,因为 9 × 3 = 27 ≡ 1 (mod 26)。这个逆元关系不是巧合,而是扩展欧几里得算法给出的必然结果:对任意与模数互素的 a,总存在唯一的 a⁻¹ 落在 1 到 25 之间。

2.3 已知明文攻击:两个字符就能反解密钥

第 1 章第 2 题给了一个更贴近攻击视角的场景:密文是edsgickxhuklzveqzvkxwkzzukvcuh,已知明文前两个字符是if,求整个明文。这其实是仿射密码最经典的已知明文攻击。设加密为 c ≡ a·m + b (mod 26),把 i=8 → e=4、f=5 → d=3 代入,得到两个同余方程:

8a + b ≡ 4 (mod 26) 5a + b ≡ 3 (mod 26)

两式相减得 3a ≡ 1 (mod 26),所以 a ≡ 9(因为 3 × 9 = 27 ≡ 1)。再回代 5 × 9 + b ≡ 3,即 45 + b ≡ 3,45 ≡ 19,所以 b ≡ 3 − 19 = −16 ≡ 10 (mod 26)。密钥 (a, b) = (9, 10) 就出来了。解密时 9⁻¹ = 3,因为 9 × 3 = 27 ≡ 1。答案里对密文逐字符算3 × (c − 10) mod 26,得到明文ifyoucanreadthisthankateahcer。这里有个细节值得注意:明文里出现了拼写错误(“teacher” 写成了 “teahcer”),但解密结果依然正确,因为仿射变换只关心数字映射,不关心语义。这也说明,已知明文攻击只需要极少的明文片段就能完全恢复密钥,仿射密码的抗攻击能力在已知明文模型下几乎为零。

提示:求模逆元时,如果 gcd(a, 26) ≠ 1,方程 3a ≡ 1 就无解,说明给定的明密文对本身不自洽,或者假设的加密模型不对。实际排查时先验证 gcd,再往下算。

3. 多表代换:矩阵密钥如何把分组密码的骨架搭起来

3.1 从标量到矩阵:分组加密的第一次抽象

仿射变换一次处理一个字符,多表代换把明文按固定长度分组,每组看成一个列向量 Mᵢ,用矩阵 A 左乘再加向量 B,得到密文分组 Cᵢ ≡ A·Mᵢ + B (mod 26)。第 1 章第 3 题给了一个 4×4 矩阵 A 和 4 维向量 B,对一段长明文做分组加密。这个结构已经非常接近现代分组密码的雏形:分组、线性变换、模运算、密钥矩阵。区别在于现代分组密码用的是非线性 S 盒和轮函数,而这里的 A 是线性映射,安全性完全依赖矩阵的可逆性和密钥空间大小。

矩阵 A 必须可逆,否则解密变换 Mᵢ ≡ A⁻¹·(Cᵢ − B) (mod 26) 不存在。答案直接给出了 A⁻¹ 的具体数值矩阵,说明题目已经保证 A 在模 26 下可逆。判断一个整数矩阵在模 26 下是否可逆,条件是行列式与 26 互素。如果 det(A) 是偶数或是 13 的倍数,A 就不可逆,加密就翻车了。这个条件和仿射变换里 gcd(a, 26)=1 是同一个数论约束在不同维度上的推广。

3.2 分组、矩阵乘法和模运算的落地细节

答案把明文按 4 个字符一组切开,最后一组不足 4 个字符时补了什么,原文没有明说,但从密文分组数量看,应该是做了填充。分组之后,每个分组 Mᵢ 是一个 4×1 列向量,A 是 4×4 矩阵,A·Mᵢ 得到 4×1 向量,再加 B,逐分量模 26。用代码表达这个流程:

import numpy as np # 模 26 下的矩阵乘法与加法 def mat_encrypt(M, A, B, mod=26): # M: 4x1 列向量,A: 4x4 矩阵,B: 4x1 向量 return (A @ M + B) % mod def mat_decrypt(C, A_inv, B, mod=26): return (A_inv @ (C - B)) % mod # 示例:单个分组的加密与解密验证 A = np.array([[3, 13, 21, 9], [15, 10, 6, 25], [10, 17, 4, 8], [1, 23, 7, 2]]) A_inv = np.array([[23, 13, 20, 5], [0, 10, 11, 0], [9, 11, 15, 22], [9, 2, 6, 25]]) B = np.array([[12], [18], [17], [0]]) M = np.array([[15], [11], [4], [0]]) # "PLEA" C = mat_encrypt(M, A, B) M_back = mat_decrypt(C, A_inv, B) # M_back 应等于 M

这里的关键是A @ M之后每个分量都要% 26,而且(C - B)可能出现负分量,% 26在 NumPy 里对数组逐元素生效,负数的结果是非负的,和 Python 标量行为一致。如果换成 C 语言实现,(C[i] - B[i]) % 26在 C 里可能返回负数,必须写成((C[i] - B[i]) % 26 + 26) % 26。这个坑在嵌入式或底层实现里非常常见,血泪经验就是:模运算的负数处理,语言之间不统一,跨语言移植时第一件事就是确认余数符号。

答案里密文分组写成NQXB BTWB DCJJ IJDT ...这样的形式,每组 4 个字母,对应一个 4 维向量。解密时用 A⁻¹ 左乘(Cᵢ − B),再模 26,逐组还原,最后拼回明文。整个过程和加密完全对称,只是矩阵换成了逆矩阵,加法换成了减法。这种“加密和解密结构相同、只换密钥材料”的设计,在 DES 和 SM4 里会以轮函数对合的形式再次出现。

3.3 矩阵密钥的安全性边界

多表代换的密钥是矩阵 A 和向量 B。A 是 4×4 矩阵,每个元素在 0 到 25 之间,理论密钥空间是 26¹⁶ 量级,但可逆矩阵只占其中一部分,实际有效密钥空间要小得多。更致命的是,它是线性变换。已知足够多的明密文对,就可以直接解线性方程组恢复 A 和 B,计算复杂度和解普通线性方程组相当。第 1 章第 4 题就是这种思路的简化版:已知明文dont加密成elni,A 是 2×2、B 是零矩阵,直接列方程解出 A 的四个元素。答案解出 A ≡ [[10, 13], [9, 23]] (mod 26)。这说明多表代换的安全性并不比仿射变换高多少,它的价值在于展示了“分组 + 矩阵 + 模运算”这套组合拳,为后面理解 DES 的 S 盒非线性、AES 的列混合(MixColumns)打下直觉基础。AES 的列混合本质上也是一个矩阵乘法,只不过它在 GF(2⁸) 上做,而且矩阵是精心设计的,保证扩散性和可逆性。

4. 流密码与 LFSR:线性递推为什么能产生伪随机序列

4.1 移位寄存器的状态转移与特征多项式

线性反馈移位寄存器(LFSR)是流密码的核心部件。n 级 LFSR 在每一拍输出一个比特,同时根据反馈函数把新比特移入寄存器。反馈函数是线性的,形式为 aₙ₊₁ = c₁aₙ ⊕ c₂aₙ₋₁ ⊕ … ⊕ cₙa₁,其中 cᵢ 是反馈系数。把这些系数写成多项式 f(x) = 1 + c₁x + … + cₙxⁿ,就是特征多项式。第 2 章第 1 题给了一个 3 级 LFSR,初始状态非零,问 4 种线性反馈函数各自的输出序列和周期。3 级意味着特征多项式是 3 次的,在 GF(2) 上 3 次多项式一共 8 个,去掉常数项为 0 的,再筛掉可约的,剩下不可约多项式对应的反馈函数才能产生最长周期序列。

答案的结论是:不可约多项式对应的输出序列是 m 序列,周期为 2³ − 1 = 7;可约多项式对应的序列周期更短。m 序列的定义就是由 n 级 LFSR 产生的、周期达到最大值 2ⁿ − 1 的序列。这个 2ⁿ − 1 不是随便来的,它是非零状态的总数:n 级寄存器有 2ⁿ 个状态,全零状态在非零反馈下会锁死,所以最多经过 2ⁿ − 1 个非零状态后必须回到起点。特征多项式不可约且本原时,状态遍历所有非零状态,周期取到最大值。

4.2 用代码验证 LFSR 的周期

以 3 级 LFSR、反馈函数 aₙ₊₃ = aₙ₊₁ ⊕ aₙ 为例,特征多项式是 x³ + x + 1,它在 GF(2) 上不可约且本原。初始状态设为 001(从右往左读,a₁=1, a₂=0, a₃=0),逐拍推演:

def lfsr(seed, taps, steps): # seed: 初始状态列表,低位在右 # taps: 反馈抽头位置(0-based) state = seed[:] seq = [] for _ in range(steps): out = state[-1] seq.append(out) fb = 0 for t in taps: fb ^= state[t] state = [fb] + state[:-1] return seq # 3 级 LFSR,反馈 a_{n+3} = a_{n+1} + a_n # 状态 [a3, a2, a1],抽头取 a1 和 a2 seq = lfsr([0, 0, 1], [0, 1], 14) # seq 前 7 位与后 7 位重复,周期为 7

taps参数指定哪几个寄存器位参与异或反馈。state = [fb] + state[:-1]这一行完成右移并把反馈比特放到最高位。跑 14 拍,前 7 拍和后 7 拍完全一致,周期 7 得到验证。如果换一个可约的特征多项式,比如 x³ + 1(对应反馈 aₙ₊₃ = aₙ),周期会短很多,具体取决于初始状态落在哪个循环里。答案里 4 种反馈函数分别对应不同的周期,有的 7,有的 3,有的 1,本质上是状态图里不同长度的环。

4.3 m 序列的游程性质与已知明文攻击

第 2 章第 5 题问的是 m 序列中形如 (Sⱼ, Sⱼ₊₁) = (1,1) 的比特对有多少个。答案用游程性质推导出 2ⁿ⁻² − n + 1。游程是指连续相同比特的段,m 序列的游程分布有严格规律:长度为 i 的游程有 2ⁿ⁻ⁱ⁻¹ 个,0 游程和 1 游程各占一半。这个性质不是巧合,而是 m 序列状态遍历的必然结果。每个非零状态恰好出现一次,相邻状态之间的输出比特关系决定了游程长度分布。

第 2 章第 6 题更直接:已知密文串和明文串,密钥流是 3 级 LFSR 产生的,破译密钥流生成器。密钥流 = 密文 ⊕ 明文,得到 1110100111。3 级 LFSR 的递推关系是 aₙ₊₃ = c₁aₙ₊₂ ⊕ c₂aₙ₊₁ ⊕ c₃aₙ,用前 6 个比特列 3 个方程就能解出 c₁、c₂、c₃。答案解出递推关系后,整个密钥流生成器就完全暴露了。这说明 LFSR 的线性性是一把双刃剑:它让序列有良好的周期和统计性质,但也让已知明文攻击变得异常简单,只要 2n 个比特就能解出 n 级 LFSR 的全部反馈系数。这也是为什么实际流密码不能直接用 LFSR 输出作为密钥流,必须加非线性滤波或组合多个 LFSR。

注意:第 2 章第 7 题讨论了只有 2n−2 个明密文对时如何破译。思路是枚举最后 2 个未知比特的 16 种组合,分别解出候选 LFSR,再用 m 序列性质筛选。这个“枚举少量未知 + 性质过滤”的思路在密码分析里很常见,代价是可能得到多个候选解,需要额外信息消歧。

5. 分组密码的对合性与错误传播:DES 和 SM4 的设计取舍

5.1 DES 的补密钥性质与穷搜索空间减半

第 3 章第 1 题证明了一个经典结论:如果对 DES 的明文和密钥同时逐比特取补,得到的密文也是原密文的逐比特取补,即 Y' = DES_{K'}(X')。证明的关键在于 DES 轮函数里 f(R, K) 的结构:扩展置换后与密钥异或,再进 S 盒。扩展置换是线性的,异或操作满足 (A ⊕ B)' = A' ⊕ B',而 S 盒对输入取补并不保持输出取补关系,但整个轮函数的输入 R 和 K 同时取补时,异或结果不变,因为补码异或等于原码异或。答案的证明正是抓住了这一点:f(R'_{i−1}, K'i) = f(R{i−1}, K_i),于是 R'_i = ¬R_i,逐轮归纳下去,最终密文取补。

这个性质对穷搜索攻击有直接影响。原本密钥空间是 2⁵⁶,现在给定明文 X,计算 Y = DES_K(X),同时就知道 DES_{K'}(X') = ¬Y。一次密钥尝试覆盖了两个密钥 K 和 K',搜索空间减半到 2⁵⁵。答案明确说“可将待搜索的密钥空间减少一半”。这不是理论上的小优化,而是实实在在的攻击加速,也是 DES 设计里被反复讨论的一个弱点。

5.2 解密变换为什么是加密变换的逆

第 3 章第 2 题用算子语言证明了 DES 解密是加密的逆。定义 T(L, R) = (R, L) 为左右交换,F_{kᵢ}(L, R) = (L ⊕ f(R, kᵢ), R)。一轮迭代是 T∘F_{kᵢ}。关键观察是 T² = I(交换两次回到原样),且 F_{kᵢ}² = I(异或同一个值两次抵消)。于是 (T∘F_{kᵢ})⁻¹ = F_{kᵢ}∘T。把 16 轮展开,加密是 IP⁻¹∘F_{k₁₆}∘T∘…∘F_{k₁}∘IP,解密是 IP⁻¹∘F_{k₁}∘T∘…∘F_{k₁₆}∘IP,两者复合得到恒等映射。这个证明的美妙之处在于它不依赖 S 盒的具体内容,只依赖 Feistel 结构的对称性。Feistel 网络之所以能让加密和解密共用同一套硬件,根源就在这里:轮函数不需要可逆,因为异或和交换本身就是可逆的。

5.3 CBC 和 CFB 的错误传播:一个错误能走多远

第 3 章第 3 题问 CBC 模式下密文分组出错的影响。CBC 解密时 Pᵢ = D(Cᵢ) ⊕ Cᵢ₋₁。如果 C₁ 有一个错误,P₁ 和 P₂ 都会受影响:P₁ 因为 D(C₁) 出错而错,P₂ 因为异或了错误的 C₁ 而错。但从 P₃ 开始,C₁ 不再参与运算,错误停止传播。所以 CBC 的错误传播长度是 2,具有自恢复能力。答案明确说“P₂ 后的分组不受影响”。反过来,如果明文 P₁ 在加密前就有一个比特错误,那么 C₁ 错,C₂ 因为异或了 C₁ 也错,一路传下去,所有密文分组都错,但解密后只有 P₁ 错,其余明文分组正确恢复。这个不对称性说明 CBC 的错误传播是“加密方向全传播、解密方向有限传播”。

第 3 章第 4 题问 8 比特 CFB 模式下一个密文字符出错的影响。CFB 解密时 Pᵢ = Cᵢ ⊕ E(Sᵢ),其中 Sᵢ 是移位寄存器内容,依赖之前的密文。一个密文比特出错,会影响当前分组的解密输出,还会进入移位寄存器,影响后续 8 个分组的密钥流生成,总共影响 9 组。答案说“错误传播的长度为 9”。这个数字来自 CFB 的反馈结构:密文反馈进寄存器,寄存器长度决定影响范围。8 比特 CFB 的寄存器是 64 比特,但按字节处理,所以影响 8 个后续分组加当前分组,共 9 组。

5.4 IDEA 的模乘实现与 SM4 的对合性

第 3 章第 5 题讨论 IDEA 里模 2¹⁶ + 1 乘法的有效实现。核心思路是把 ab 写成 q(2ⁿ + 1) + r,利用 2ⁿ ≡ −1 (mod 2ⁿ + 1) 的性质,把大数乘法转化成低位和高位的组合运算。答案推导出 r = (ab mod 2ⁿ) − (ab div 2ⁿ),当 q + r ≥ 2ⁿ 时还要加 2ⁿ + 1 修正。这个技巧在软件实现里很实用,因为 2ⁿ 的模和除都可以用位运算完成,避免了真正的除法指令。第 6 题进一步解释为什么模乘选 2¹⁶ + 1 而不是 2¹⁶:2¹⁶ + 1 是素数,非零元素构成乘法群;2¹⁶ 不是素数,有零因子,不构成群。模加选 2¹⁶ 是因为要和模乘区分开,保证加法和乘法不在同一个群内,避免代数结构上的退化。

第 3 章第 7 题证明 SM4 的对合性。SM4 的轮函数由加密函数 G 和交换 E 组成,G 对数据做 T 变换后异或轮密钥,E 交换字顺序。证明的关键是 G 满足 G² = I(异或同一个轮密钥两次抵消),E 也满足 E² = I。于是整个轮函数 F = G∘E 满足 F² = I,加密 32 轮和解密 32 轮只是轮密钥顺序相反。答案把加密写成 G₀EG₁E…G₃₀EG₃₁R,解密写成 G₃₁EG₃₀E…G₁EG₀R,两者结构完全相同,只是密钥下标倒序。这个性质和 DES 的 Feistel 对合性异曲同工,但 SM4 用的是非平衡 Feistel 结构,轮函数本身设计成对合的,实现上加密解密可以共用同一段代码,只改密钥调度。

6. 数论底座:RSA 和本原根背后的模运算机制

6.1 扩展欧几里得算法与模逆元

第 4 章第 4 题用扩展欧几里得算法求逆元,答案给了一张循环表,记录 q、x₁、x₂、x₃、y₁、y₂、y₃ 的迭代过程。扩展欧几里得的核心是在辗转相除的同时维护贝祖系数,最终得到 ax + by = gcd(a, b)。当 gcd = 1 时,x 就是 a 的模 b 逆元。答案的表格里,每一行的 q 是商,x 和 y 两组系数交替更新,最后 x₃ = 1 时对应的 x₁ 就是逆元。这个算法在 RSA 密钥生成里是必备工具,因为私钥 d 就是 e 模 φ(n) 的逆元。

def egcd(a, b): # 返回 (g, x, y) 使得 a*x + b*y = g if b == 0: return a, 1, 0 g, x1, y1 = egcd(b, a % b) return g, y1, x1 - (a // b) * y1 def mod_inv(a, m): g, x, _ = egcd(a, m) if g != 1: raise ValueError("逆元不存在") return x % m # 验证:11 在模 26 下的逆元 print(mod_inv(11, 26)) # 输出 19

递归版扩展欧几里得代码短,但递归深度和模数比特数成正比,RSA 里模数是 1024 位以上,递归深度上千,实际实现一般用迭代版。x % m保证逆元落在 0 到 m−1 之间,因为递归返回的 x 可能是负数。这个细节在实现 RSA 时如果漏掉,私钥 d 可能是负的,签名验证就会失败,而且报错信息往往很隐晦,排查起来相当费劲。

6.2 中国剩余定理加速 RSA 解密

第 4 章第 11 题证明用中国剩余定理(CRT)改进 RSA 解密可以把速度提升约 4 倍。RSA 解密是 m = c^d mod n,n = p·q,p 和 q 各约 n 的一半比特。直接算 c^d mod n 的代价是 O(log³n)。用 CRT 拆成 c^d mod p 和 c^d mod q 两个模指数,每个的模长减半,代价变成 2 × O((log n / 2)³) = O(log³n)/4。答案的证明正是这个思路:两个半长模指数的总时间是原时间的四分之一,所以速度提升 4 倍。实际实现里还要加上 CRT 组合的计算代价,但组合只是两次乘法和一次加法,相比模指数可以忽略。OpenSSL 等库的 RSA 私钥运算默认走 CRT 路径,原因就在这里。

6.3 本原根与离散对数:为什么模 p 乘法群是循环群

第 4 章第 8 题求模 25 的所有本原根。本原根 g 的定义是 g 的幂能生成整个乘法群,即 ord(g) = φ(25) = 20。答案先分解 20 = 2² × 5,然后对每个候选 g 检查 g²⁰/² ≠ 1 且 g²⁰/⁵ ≠ 1,满足的就是本原根。最终得到 8 个本原根。这个检查方法的依据是:如果 g 的阶不是 20,那么它一定是 20 的某个真因子的倍数,而 20 的真因子最大的是 10 和 4,所以只需排除 g¹⁰ = 1 和 g⁴ = 1 两种情况。答案里逐个数计算 g¹⁰ 和 g⁴,筛出满足条件的 g。本原根的存在性保证了模素数 p 的乘法群是循环群,这是 Diffie-Hellman 密钥交换和 ElGamal 加密的安全基础:离散对数问题在循环群里被认为是困难的,而群阶的因子分解结构直接影响攻击复杂度。

第 4 章第 9 题证明 Zₙ 是域当且仅当 n 是素数。证明分两步:如果 n 是素数,每个非零元素都与 n 互素,存在逆元,构成域;如果 n 不是素数,存在非平凡因子,某个非零元素没有逆元,不构成域。这个结论把“模运算的可逆性”和“代数结构”联系起来了,仿射变换里 gcd(a, 26)=1 的要求,本质上就是要求 a 落在 Z₂₆ 的可逆元集合里,而 26 不是素数,所以可逆元只是一部分,不是全部。

7. 从习题到机制:可逆性、线性度和扩散性怎么权衡

把这份习题参考答案从头翻到尾,会发现一个反复出现的主题:可逆性不是免费的。仿射变换要求 a 与 26 互素,多表代换要求矩阵行列式与 26 互素,IDEA 的模乘选 2¹⁶+1 是为了让非零元素构成群,RSA 要求 e 与 φ(n) 互素。每一个“能解密”的保证,背后都是一个数论条件在兜底。而一旦为了可逆性选了线性结构,安全性就要打折扣:仿射变换和多表代换都是线性的,已知明文攻击可以直接解方程;LFSR 是线性的,2n 个比特就能反解反馈系数。DES 和 SM4 之所以要加 S 盒和非线性变换,就是为了打破线性性,让已知明文攻击不能直接列方程。

另一个值得琢磨的点是错误传播。CBC 在解密方向只传播 2 组,CFB 传播 9 组,ECB 不传播。这个差异不是实现细节,而是模式设计时的主动取舍:CBC 牺牲了一点错误传播换取链接依赖,CFB 用更长的传播换取自同步能力。实际选模式时,如果信道误码率高,ECB 或 CBC 更稳;如果要求逐字节处理且能容忍错误扩散,CFB 或 OFB 更合适。这些取舍在习题里以“问答题”的形式出现,但背后的机制和真实系统设计是同一套逻辑。

我自己的习惯是,每次碰到一个密码算法或模式,先问三个问题:它的可逆性靠什么数论条件保证?它的非线性从哪里来?它的错误或攻击传播边界在哪里?这三个问题答不上来,就说明还没真正理解这个机制,只是记住了结论。这份习题集的价值不在于答案本身,而在于它把每个机制的推导过程摊开了,让你能看到条件、方程和验证步骤之间的咬合关系。从那以后我每次读密码学材料,都会先把里面的模运算和可逆性条件单独拎出来推一遍,推不通的地方就是理解的黑匣子。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询