ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

密码编码学与网络安全第五版答案:从习题到Python实战

密码编码学与网络安全第五版答案:从习题到Python实战

简介:这份《密码编码学与网络安全(第五版)答案》面向信息安全、密码学课程的学习者与备考学生,用于对照教材习题、梳理章节重点与验证解题思路。资源为单个doc文档,压缩包约231KB,内容按教材章节组织,从经典加密技术、DES与AES对称加密、有限域,到RSA公钥密码、密钥管理、消息认证与散列函数,再到数字签名、电子邮件安全、IPSec、Web安全、入侵检测、恶意软件与防火墙等网络安全主题,并附有各章习题参考答案,便于按目录逐章检索与复习。目前已有61人学习下载,适合课堂同步练习、期末复习与考研自测时作为参考,帮助读者快速定位知识点、核对答案并理解密码学与网络安全的核心概念与算法原理。

1. 密码编码学与网络安全(第五版)答案.doc:一份习题答案到底该怎么用

很多人第一次搜「密码编码学与网络安全(第五版)答案.doc」,场景其实很具体:课程讲到分组密码那一章,课后题里让你手算 S-DES 的一轮变换、推 RSA 的私钥指数、证明某个协议不满足前向保密,卡了两小时没思路,于是想找一份答案对照。问题在于,直接下载到的 .doc 文件往往只有最终数字,没有中间步骤,抄完还是不会。这份材料真正有价值的地方不是「答案」两个字,而是它背后对应的一整套经典密码学知识体系:古典密码、分组密码、公钥密码、哈希与消息认证、密钥管理、认证协议、网络层与应用层安全。把它当成一份「习题索引」而不是「抄写模板」,才是正确的打开方式。这篇文章面向三类人:正在跟这门课、需要把课后题吃透的学生;准备把密码学基础补起来、要动手写加解密代码的开发者;以及需要快速核对协议设计是否踩了经典坑的工程人员。下面从知识地图、动手复现、参数设置、避坑排查一路讲到怎么用答案反推考点。

2. 先看清这份答案覆盖的知识地图:七块内容与它们的依赖关系

在动手翻答案之前,得先知道这份材料大致按什么顺序组织。经典教材的章节编排不是随意的,它有一条清晰的依赖链:没有数论基础就推不动 RSA,不理解分组模式和填充就写不对 AES 的封装,不清楚哈希的碰撞性质就设计不出安全的 MAC。所以第一步是把答案里的题目按知识块归类,而不是按页码顺序刷。

2.1 七块核心内容与典型题型

把常见版本的内容拆开,大致是这么七块,每块对应的题型和难度差异很大:

知识块典型题型依赖前置手算可行性
古典密码仿射密码、维吉尼亚、Playfair 加解密模运算完全可手算
分组密码S-DES 单轮、DES 轮函数、AES 字节代换置换、有限域单轮可手算
公钥密码RSA 密钥生成、ElGamal、Diffie-Hellman数论、模幂小数字可手算
哈希与 MAC生日攻击概率、HMAC 结构概率、异或可估算
密钥管理密钥分发、证书链验证公钥基础概念为主
认证协议重放攻击、中间人分析协议逻辑逻辑推演
网络与系统安全IPSec、TLS 握手、防火墙规则前六块综合配置为主

这张表的作用是帮你判断:哪些题必须自己算一遍才有手感,哪些题看懂逻辑即可。RSA 的密钥生成如果只背公式,考试换个数字就废;而防火墙规则这类题,理解匹配顺序比背答案重要得多。

2.2 为什么不能按页码顺序刷

一个血泪经验:按页码刷题的人,往往在古典密码上花太多时间,到公钥密码时已经没精力了。正确的做法是先做依赖链底层的题,把模运算、异或、置换这三样基本功练熟,后面所有章节都会加速。具体来说,先集中处理古典密码里的模运算题,再跳到分组密码的置换和有限域运算,最后攻公钥密码的模幂和扩展欧几里得。这个顺序和教材章节顺序基本一致,但你要主动跳过那些纯记忆性的概念题,把时间留给需要动手的。

2.3 用答案反推考点的三个动作

拿到一份答案,不要直接看结果。我一般会做三个动作:第一,遮住答案自己先写一遍,哪怕写错;第二,对照答案时只核对关键中间步骤,比如 RSA 里的 φ(n) 算对没有、DES 里的扩展置换有没有错位;第三,把答案里的数字换掉,自己重新出一遍题。第三个动作最费时间,但效果最好——能自己出题,说明你真的理解了算法结构,而不是记住了某道题的结果。对于「密码编码学与网络安全(第五版)答案.doc」这类材料,它的最大价值就在第三步:给你一个标准答案作为参照,让你能验证自己改数字后算得对不对。

3. 把答案里的算法跑起来:用 Python 复现四类核心题型

光看答案不动手,过一周就忘。这一章把答案里最常见的四类题——仿射密码、S-DES 单轮、RSA 密钥生成、HMAC——用 Python 复现一遍。每段代码都可以直接跑,跑完再对照答案里的数字,你会发现很多「答案对不上」的情况其实是自己中间步骤错了。

3.1 仿射密码:模逆元是第一个拦路虎

仿射密码的加密是 E(x) = (ax + b) mod 26,解密要先求 a 关于 26 的模逆元。答案里经常直接给出逆元,但不告诉你怎么算。下面这段代码把加密、解密、求逆元都写全了:

# 仿射密码:加密、解密与模逆元求解 def egcd(a, b): # 扩展欧几里得算法,返回 (g, x, y) 使得 ax + by = g if b == 0: return a, 1, 0 g, x1, y1 = egcd(b, a % b) return g, y1, x1 - (a // b) * y1 def mod_inverse(a, m): # 求 a 在模 m 下的逆元,要求 gcd(a, m) == 1 g, x, _ = egcd(a, m) if g != 1: raise ValueError(f"{a} 在模 {m} 下没有逆元") return x % m def affine_encrypt(text, a, b): # 只处理小写字母,其余字符原样保留 result = [] for ch in text: if ch.islower(): x = ord(ch) - ord('a') result.append(chr((a * x + b) % 26 + ord('a'))) else: result.append(ch) return ''.join(result) def affine_decrypt(text, a, b): a_inv = mod_inverse(a, 26) result = [] for ch in text: if ch.islower(): y = ord(ch) - ord('a') result.append(chr((a_inv * (y - b)) % 26 + ord('a'))) else: result.append(ch) return ''.join(result) # 测试:a=5, b=8,验证加解密互逆 cipher = affine_encrypt("hello", 5, 8) print("密文:", cipher) print("明文:", affine_decrypt(cipher, 5, 8))

这段代码的关键在mod_inverse。仿射密码要求 a 与 26 互质,所以 a 只能取 1、3、5、7、9、11、15、17、19、21、23、25 这些值。答案里如果出现 a=2 或 a=13,那一定是题目本身有问题,因为这两个值和 26 不互质,解密不唯一。参数 b 可以取 0 到 25 任意值,不影响可逆性。跑完这段代码,再回头看答案里的仿射密码题,你会立刻发现哪些题是「凑数」的、哪些题在考逆元计算。

3.2 S-DES 单轮:置换和异或的顺序不能乱

S-DES 是 DES 的教学简化版,答案里经常让你手算一轮的完整过程。手算容易错的地方是置换顺序和异或的时机。下面用代码把一轮拆开:

# S-DES 单轮:IP 置换、轮函数 F、异或 IP = [2,6,3,1,4,8,5,7] EP = [4,1,2,3,2,3,4,1] P4 = [2,4,3,1] S0 = [[1,0,3,2],[3,2,1,0],[0,2,1,3],[3,1,3,2]] S1 = [[0,1,2,3],[2,0,1,3],[3,0,1,0],[2,1,0,3]] def permute(bits, table): # 按 table 里的位置(1-based)重排 bits return [bits[i-1] for i in table] def sbox(bits, box): # 4 位输入:首尾两位选行,中间两位选列 row = bits[0]*2 + bits[3] col = bits[1]*2 + bits[2] val = box[row][col] return [int(b) for b in format(val, '02b')] def f_function(right, key): # 轮函数:EP 扩展 -> 与子密钥异或 -> S 盒 -> P4 置换 expanded = permute(right, EP) xored = [expanded[i] ^ key[i] for i in range(8)] left_s = sbox(xored[:4], S0) right_s = sbox(xored[4:], S1) return permute(left_s + right_s, P4) # 示例:8 位明文和 8 位密钥 plain = [1,0,1,1,1,1,0,1] key = [1,0,1,0,0,1,0,1] p = permute(plain, IP) left, right = p[:4], p[4:] new_left = [left[i] ^ f_function(right, key)[i] for i in range(4)] print("一轮后左半:", new_left, "右半:", right)

这里最容易翻车的是 S 盒的查表方式:输入 4 位,第 1 位和第 4 位拼成行号,第 2 位和第 3 位拼成列号。很多答案里直接写结果,不写这个规则,导致你对着数字看不出怎么来的。另外注意f_function里先扩展再异或,顺序反了结果完全不同。跑一遍代码,再手算一遍,两边对上,这个知识点才算真正拿下。

3.3 RSA 密钥生成:小数字手算和代码验证要交叉做

RSA 的课后题通常给两个小素数,让你算 n、φ(n)、公钥指数 e 和私钥指数 d。手算 d 要用扩展欧几里得,很容易算错。代码验证是必须的:

# RSA 密钥生成:小素数手算验证 def rsa_keygen(p, q, e): n = p * q phi = (p - 1) * (q - 1) g, d, _ = egcd(e, phi) if g != 1: raise ValueError("e 与 φ(n) 不互质,换一个 e") d = d % phi return n, phi, e, d def rsa_encrypt(m, e, n): return pow(m, e, n) def rsa_decrypt(c, d, n): return pow(c, d, n) # 经典小数字:p=61, q=53, e=17 n, phi, e, d = rsa_keygen(61, 53, 17) print(f"n={n}, φ(n)={phi}, e={e}, d={d}") m = 65 c = rsa_encrypt(m, e, n) print("密文:", c, "解密:", rsa_decrypt(c, d, n))

参数选择上有几个硬约束:p 和 q 必须是素数且不相等;e 要与 φ(n) 互质,常见取值是 65537,但小数字题里常用 17 或 3;d 是 e 关于 φ(n) 的模逆元。答案里如果 d 算出来是负数,记得取模转正。这段代码跑通后,你可以把 p、q 换成任意小素数,自己出题自己验证,比刷十道现成题都管用。

3.4 HMAC:为什么答案里的密钥长度有讲究

HMAC 的题经常问:密钥比分组长度长怎么办、短怎么办。答案里可能只给结论,但你要理解背后的填充规则。下面这段代码演示 HMAC 的核心结构:

import hashlib def hmac_demo(key: bytes, msg: bytes, block_size=64): # 密钥长于分组先哈希,短于分组则右侧补零 if len(key) > block_size: key = hashlib.sha256(key).digest() key = key.ljust(block_size, b'\x00') ipad = bytes([0x36] * block_size) opad = bytes([0x5c] * block_size) inner = hashlib.sha256(bytes(a ^ b for a, b in zip(key, ipad)) + msg).digest() return hashlib.sha256(bytes(a ^ b for a, b in zip(key, opad)) + inner).hexdigest() print(hmac_demo(b"secret", b"message"))

关键参数是block_size,SHA-256 的分组长度是 64 字节。密钥处理规则是:长了先哈希压缩,短了补零到分组长度。ipad 是 0x36 重复,opad 是 0x5c 重复,这两个常数不能改。答案里如果问「为什么 HMAC 比直接哈希(key || msg) 安全」,核心原因是后者存在长度扩展攻击,而 HMAC 的双层结构阻断了这种攻击。跑一遍代码,再对照答案里的结构图,理解会深很多。

4. 参数怎么设:从答案里提炼出的五组关键数值

答案里的数字不是随便给的,每一组都对应一个约束条件。这一章把最常见的五组参数单独拎出来,说清楚取值范围和越界后果。

4.1 模运算里的互质条件

仿射密码的 a 与 26 互质、RSA 的 e 与 φ(n) 互质、Diffie-Hellman 的底数与本原根相关——这些「互质」要求本质上是保证逆元存在。判断方法很简单:gcd 等于 1 就行。实际做题时,如果答案里出现不互质的情况,要么是题目印刷错误,要么是你抄错了数字。我一般会先用代码算一遍 gcd,再动手解题,避免白算。

4.2 分组长度与密钥长度的匹配

DES 分组 64 位、密钥 56 位有效;AES 分组 128 位、密钥 128/192/256 位;S-DES 分组 8 位、密钥 10 位。答案里如果让你算轮数,DES 是 16 轮,AES-128 是 10 轮,AES-256 是 14 轮。这些数字要记牢,因为很多题是围绕「为什么是这个轮数」设计的。轮数太少抗不住差分分析,太多则性能下降,这是设计时的权衡。

4.3 公钥指数 e 的常见取值

e 取 3 加密快但有小指数攻击风险,取 65537 是当前最常见的选择,兼顾速度和安全性。小数字题里 e 常取 17 或 7,纯粹是为了手算方便。答案里如果 e 取 3 且明文很短,要注意可能存在的低指数攻击——当 m^3 < n 时,直接开立方就能还原明文。这个坑在考试里经常出现。

4.4 哈希输出长度与生日攻击概率

SHA-1 输出 160 位,SHA-256 输出 256 位。生日攻击的碰撞概率近似为 1 - e^(-k²/(2N)),其中 N 是输出空间大小。答案里如果问「多少次尝试能有 50% 碰撞概率」,对 160 位输出大约是 2^80 次。这个估算方法要会,因为它是理解哈希安全强度的基础。

4.5 协议里的随机数长度

认证协议题里经常出现 nonce 和会话密钥。nonce 的长度要足够防重放,一般至少 128 位;会话密钥长度与对称算法匹配,AES 用 128 位或 256 位。答案里如果 nonce 只有 32 位,那这道题大概率是在考「为什么这样不安全」。看到短随机数,先想重放攻击和碰撞。

5. 避坑与排查:五条血泪经验

这一章单独列出来,因为下面这些坑我几乎每个都踩过,而且答案里通常不会写。

5.1 现象:答案数字对不上,差一位

原因:置换表的下标是 1-based 还是 0-based 没统一。DES 和 S-DES 的官方表格都是 1-based,但代码里数组是 0-based,转换时容易差一位。解决:写一个permute函数统一处理,输入输出都用列表,表格里的数字直接当 1-based 用,函数内部减一。所有置换题都走这个函数,不要手写下标。

5.2 现象:RSA 解密结果乱码

原因:明文 m 大于 n,或者加密前没有做填充。RSA 的数学前提是 m < n,实际使用必须配合 OAEP 等填充方案。解决:小数字题里确保 m < n;工程实现里永远不要裸 RSA,必须加填充。答案里如果直接对长消息做 RSA,那是在简化教学,别照搬到生产。

5.3 现象:HMAC 验证失败但密钥没错

原因:消息编码不一致,比如一边用 UTF-8 一边用 GBK,或者多了换行符。解决:把消息和密钥都转成 bytes 再比较,调试时打印十六进制。这个坑在跨语言实现时特别常见,Python 和 Java 的默认编码不同,很容易翻车。

5.4 现象:Diffie-Hellman 协商出的密钥和别人一样

原因:用了固定的私钥,或者随机数生成器被预测。DH 的安全性依赖私钥的随机性,如果每次都用同一个私钥,中间人可以直接算出共享密钥。解决:每次协商都生成新的随机私钥,随机源要用密码学安全的。答案里如果私钥是固定值,那是在演示流程,不是让你照抄。

5.5 现象:分组密码加密后长度不是分组的整数倍

原因:没有做填充,或者填充方案不对。PKCS#7 填充是补到分组长度,补的字节值等于补的字节数。解决:加密前先填充,解密后去掉填充并校验。答案里如果问填充相关,重点看 PKCS#7 和零填充的区别——零填充无法区分真实零字节和填充零字节,所以不推荐。

6. 把答案变成自己的题库:三个进阶技巧

最后一章说点更实用的。一份答案的价值上限,取决于你怎么用它。下面三个技巧是我自己从「刷题」过渡到「出题」的过程中总结的,适合已经能把前面代码跑通的人。

6.1 用脚本批量生成同类题并自动验证

与其一道一道刷,不如写个脚本批量生成。以 RSA 为例,随机生成两个小素数,随机选 e,自动算 d,然后加密一个随机明文再解密,验证是否还原。跑一百组,只要有一组失败,就说明你的实现有边界问题。这个方法的效率远高于手算,而且能覆盖到很多答案里没有的边界情况。

import random def is_prime(x): if x < 2: return False for i in range(2, int(x**0.5) + 1): if x % i == 0: return False return True def random_rsa_test(trials=100): primes = [p for p in range(11, 200) if is_prime(p)] for _ in range(trials): p, q = random.sample(primes, 2) n, phi = p * q, (p - 1) * (q - 1) e = random.choice([3, 5, 7, 11, 13, 17, 19, 23]) g, d, _ = egcd(e, phi) if g != 1: continue d %= phi m = random.randint(2, n - 1) assert rsa_decrypt(rsa_encrypt(m, e, n), d, n) == m, f"失败: p={p}, q={q}, e={e}" print(f"{trials} 组测试全部通过") random_rsa_test()

这段代码的关键是assert那一行:只要有一组不还原,立刻报错并打印参数。跑通一百组,你对 RSA 的理解就超过只看答案的人了。

6.2 把协议题画成消息序列图再对照答案

认证协议题光看文字容易漏步骤。我一般会先自己画一遍消息序列图,标出每条消息的发送方、接收方、包含的随机数和加密方式,然后再对照答案。如果答案里的步骤和你的图对不上,先怀疑自己漏了哪条消息,而不是直接改图。这个习惯能帮你发现很多「看起来对、其实少了认证步骤」的协议漏洞。

6.3 用已知攻击反推设计缺陷

答案里经常问「这个协议有什么问题」。与其背结论,不如自己构造攻击。比如重放攻击,你就想:如果我把第三条消息原样再发一次,服务器会怎么处理?如果服务器没有 nonce 校验,就会重复执行操作。再比如中间人攻击,你就想:如果攻击者能替换公钥,后续所有加密都形同虚设。用攻击者的视角看协议,比用设计者的视角更容易发现漏洞。

6.4 一个具体技巧:把答案里的数字换成符号再推一遍

最后一个技巧最费时间但最有效:把答案里的具体数字全部换成符号,重新推一遍公式。比如 RSA 的 d 计算,答案里是具体数字,你换成 p、q、e 重新推,得到 d ≡ e^(-1) mod (p-1)(q-1)。这个过程能帮你把「记住某道题」变成「理解一个公式」。我当年备考时,把 DES 的轮函数用符号推了三遍,之后所有相关题都能现场推,不用背。

希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进