外观
现代密码学算法与数字签名体系
约 14497 字大约 48 分钟
网络安全技术加密技术对称加密非对称加密
2026-05-17
本课核心
你是不是也有过这种困惑:别人说"数据加密了,很安全",但你完全不知道这到底意味着什么?本课的目标就是让你不再只是"听说某个算法很厉害",而是真正理解:加密到底在保护什么、每种算法各解决什么问题、你亲手跑一遍命令之后能看到什么证据。
我们会从 AES、RSA、Hash、数字签名、PGP 一路做到 PKI 证书链,每学一个算法,就做一个实验。你要得到的不只是"会敲命令",而是能根据自己的实验输出,向别人解释清楚"为什么这样就算安全了"。
课堂红线
所有密钥、证书、文件都只能使用课堂临时生成的测试材料。禁止尝试破解他人密码、伪造真实证书、解密他人通信,或把课堂密钥用于任何真实系统。
一、换个角度想:密码技术到底在解决什么问题
先别急着看算法名称。我们从一个最朴素的场景出发——
你给朋友发了一条消息:"明天下午3点,老地方见"。这条消息在网络上传输的时候,会经过你家的路由器、运营商的交换机、可能还有咖啡店的公共 WiFi,中间经过的每一台设备,理论上都能看到这条消息的内容。
那你可能会想:要是有人偷看怎么办?要是有人把消息里的"3点"改成"5点"怎么办?要是有人冒充你发消息怎么办?
你看,就这么一条简单的消息,其实已经牵扯出三个安全需求了:
第一,不希望别人看到内容——这就是"保密性"。对应到密码技术上,就是加密要做的事。
第二,不希望内容被偷偷改掉——这就是"完整性"。对应到密码技术上,就是 Hash 和数字签名要做的事。
第三,不希望别人冒充你——这就是"身份认证"。对应到密码技术上,就是证书和 PKI 要做的事。
还有一个更微妙的需求:你发了消息之后,万一出了什么事,你不能说"我没发过"。这就是"不可否认性"——数字签名也能解决这个问题。
所以密码技术不是一堆名词的堆砌,而是围绕着这四个真实需求生长出来的工具集。理解了这四个需求,再去学 AES、RSA、SHA-256 这些具体算法,你就知道它们各自在解决哪一个环节的问题了。
三个前提事实,让你看清密码技术的分量
在正式开始学算法之前,我们先看三件已经发生过的事。这不是讲大道理,而是让你感受到:密码技术不是实验室里的玩具,它直接影响国家安全、企业合规和你将来的就业选择。
事实一:《中华人民共和国密码法》已经把"密码"写进了法律
2019 年通过的密码法,第二条就把"密码"定义为:采用特定变换的方法对信息等进行加密保护、安全认证的技术、产品和服务。
注意这里有两个关键词:"加密保护"和"安全认证"。法律并不认为密码只是"把文字变乱",而是同时涵盖了身份认证、密钥管理、密码产品和服务。换句话说,你将来在工作中碰到的密码问题——比如给系统配置 HTTPS 证书、设计用户登录的密码存储方案、选择 VPN 的加密套件——这些都属于密码法的管辖范围。如果你毕业后进入政府单位、金融机构、关键基础设施行业,你的每一个技术选型都可能涉及合规审查。
事实二:网络安全等级保护制度把密码技术嵌入到了每一个安全环节
你在这门课的前几章已经接触过等保的概念了。等保 2.0 在身份鉴别、通信传输、数据存储、安全审计等十几个控制点里,都明确要求使用密码技术。这意味着什么呢?意味着密码不是可选项。你将来参与开发的任何一个需要过等保的系统,从登录模块到数据库加密,从 API 通信到日志完整性——全都绕不开密码。
对你这门课的意义是:你今天学的 AES、SHA-256、数字签名,都不是"理论知识点",而是等保测评报告里会逐项检查的技术要求。
事实三:王小云团队的 MD5 碰撞攻击,终结了"算法安全神话"
2004 年,中国密码学家王小云在国际密码大会上公开展示了 MD5 的碰撞攻击。她用实际数据证明了:可以人为构造两份内容不同的文件,让它们产生完全相同的 MD5 摘要。
这件事对密码学界的冲击非常大。在那之前,很多人默认"Hash 值一样就等于原始内容一样"。王小云的工作告诉大家一个残酷的真相:算法是会过期的。MD5 当年也是经过严格评审的标准算法,但计算能力在发展、攻击方法在进步,曾经安全的算法可以在一夜之间变成不安全的。
对你这门课的意义是:不要迷信"用某某算法就一定安全"。我们今天在实验里还会用 md5sum 命令,但只是为了历史对比,让你亲眼看看 MD5 的输出长什么样,然后记住:真正需要做安全证明的时候,用 SHA-256 或更强的算法。
这三个事实放在一起,传递的信息是一致的:密码技术是严肃的工程基础设施,受法律约束、被标准引用、也会随时间老化。它不是"高级技巧",而是安全从业者的基本功。
二、拆分一个加密系统:四个零件,缺一不可
现在我们从抽象回到具体。一个最简单的加密系统,只需要四个东西就能运转:
- 明文:你要保护的那份数据。可以是一段文字,也可以是一个文件、一条 HTTP 请求、一条数据库记录。
- 密文:加密之后谁都看不懂的数据。攻击者可以拿到密文,但只要没有密钥,他无法还原。
- 算法:一套公开的数学规则,告诉计算机"怎么把明文变成密文、怎么变回来"。重点是"公开"——现代密码学的基本原则是,算法的安全性不依赖算法本身的保密,而是依赖密钥的保护。换句话说,你可以把 AES 的算法细节贴到墙上,全世界都知道,但只要密钥没泄露,密文就是安全的。
- 密钥:控制加解密结果的一串秘密参数。同一份明文,用不同的密钥加密,会得到完全不同的密文。
把这四个零件串起来,就是密码学最基本的公式:
E(K,P)=CD(K,C)=P
其中 P 是明文(Plaintext),C 是密文(Ciphertext),E 是加密函数,D 是解密函数,K 是密钥。注意 E 和 D 用的是同一把 K——这就是"对称"的含义。
看起来很简单对吧?但真正的学问在细节里。你再往下想一步:
- 密钥从哪里来?如果是随机生成的,这个"随机"到底有多随机?用当前时间做种子的随机数够不够安全?
- 密钥生成之后放在哪?写在代码里?存在配置文件里?还是用专门的硬件安全模块保管?
- 密钥用多久换一次?万一泄露了怎么吊销?离职员工拿到过的密钥还能继续用吗?
这些就是"密钥生命周期管理"要回答的问题。一个密码系统是不是靠谱,很多时候不看它用的是什么算法名,而是看它的密钥管理有没有漏洞。你毕业之后如果在甲方做安全运维,大量的日常工作量其实就在管密钥——轮换证书、吊销泄露的私钥、审计谁访问过密钥管理服务。
三、加密到底用在哪:两个维度看清全貌
加密的应用场景非常多,但如果按"加密的位置"来分,其实就两大类:
传输加密:数据在发送端加密,经过网络传输,在接收端解密。中间的链路上,不管经过多少台路由器、交换机,看到的都是密文。
这里面又可以细分:
链路加密:只保护相邻两跳之间的链路。比如你的笔记本到公司路由器这一段是加密的,但路由器收到之后会先解密、再转发。这种方式的局限很明显——中间的路由器能看到明文。如果你不信任那个路由器(比如它在另一个国家),链路加密就不够。
端到端加密:数据从发送者手里加密,直到最终接收者手里才解密。中间经过的任何设备都只能转发密文。这是最彻底的传输保护方式,典型代表就是 PGP 邮件加密和 Signal 这类端到端加密通讯工具。代价是密钥管理更复杂——你得安全地拿到对方的公钥。
存储加密:数据保存在磁盘或数据库里的时候是密文形态。即使硬盘被偷走,没有密钥也读不出内容。
这又分成几种:
- 全盘加密:整个硬盘都是密文,操作系统启动时用密码解锁。Windows 的 BitLocker、macOS 的 FileVault 就是这个思路。
- 文件级加密:只加密特定的文件或文件夹。
- 数据库加密:只加密数据库里的敏感字段,比如身份证号、银行卡号。
你学的这门课侧重于传输加密——后续的 VPN、HTTPS 分析都建立在这个基础上。但你要知道,真实的安全方案往往是传输加密和存储加密同时使用的:传输过程中用 TLS 保护,落到数据库里再用 AES 加密敏感字段。两层保护,各管一段。
四、先看看古人怎么加密:古典密码的智慧与局限
在冲进 AES 和 RSA 之前,我们先花十分钟看一眼古人是怎么加密的。这不是考古——理解古典密码的"为什么不够安全",能帮你建立评判现代密码好坏的直觉。
凯撒密码:两千年前的"替代"思路
公元前一世纪,凯撒在军事通信中用了这样一种方法:把字母表统一往后挪三位。A 写成 D,B 写成 E,C 写成 F……到了最后三个字母绕回来,X 写成 A,Y 写成 B,Z 写成 C。
比如原文是 ATTACK,加密后就是 DWWDFN。
你一眼就能看出它的思路:替代——把原文里的字符,按一个固定的规则替换成另一个字符。接收方知道规则(向后挪 3 位),反向操作就能还原。
这个方法在当时确实管用。凯撒的敌人截获了 DWWDFN,他们没有密码本、不知道规则,看到的是一堆无意义的字母。而且当时识字的人本来就少,更别说密码分析了。
但放在今天,凯撒密码连幼儿园级别的安全都算不上。它的致命缺陷有三个:
第一,密钥空间极小。 凯撒密码的"密钥"就是那个偏移量。英文字母只有 26 个,偏移量最多 25 种(偏移 0 等于没加密,偏移 26 等于绕回原点)。你甚至不需要计算机——手算 25 次,总有一次是对的。这就是我们前面说的"密钥空间"问题:空间越小,暴力穷举越容易。
第二,字母频率出卖一切。 不管你把 A 替换成什么,英语里最常见的字母是 E 这一点不会变。你把密文里出现次数最多的那个字母找出来,它大概率对应明文的 E。同理,出现最多的双字母组合大概率是 TH,最多的三字母组合大概率是 THE。这种分析手法叫频率分析——九世纪的阿拉伯密码学家就已经在用了。
第三,替代规则太有规律。 偏移 3 意味着整个字母表是一个整齐的平移——A 到 D、B 到 E、C 到 F……一旦攻击者猜出一个字母的对应关系(比如用频率分析猜出密文里最常见的字母对应 E),整个规则就瞬间崩塌。为什么?因为规律太简单了,破解一个点就等于破解了全部。
从凯撒到维吉尼亚:加一把"钥匙"来抵抗频率分析
凯撒密码被频率分析轻松攻破,后人自然就想:能不能让替代规则不是固定平移,而是跟着一个"关键词"变化?
维吉尼亚密码(Vigenère cipher,16 世纪)就是这么干的。你选一个关键词,比如 KEY。加密的时候,第一个字母用 K 对应规则(A 变 K,B 变 L……),第二个字母用 E 对应规则,第三个字母用 Y 对应规则,第四个字母又回到 K 对应规则——循环使用。
同一个字母 A,在原文的不同位置,可能被加密成 K,也可能被加密成 E,也可能被加密成 Y。频率分析一下就难用了——因为同一个明文字母不再对应同一个密文字母。
这确实是个进步。维吉尼亚密码一度被称为"不可破译的密码"。但它的安全性建立在"攻击者不知道你的关键词"上。一旦关键词被猜到或者通过统计分析推断出关键词的长度,整个系统就塌了。19 世纪的英国数学家巴贝奇和德国军官卡西斯基各自独立发明了破解维吉尼亚密码的方法——核心思路就是先推测关键词的周期长度,然后按周期分组,每一组单独做频率分析。
置换加密:不是替换字符,而是打乱位置
除了"把字母变成另一个字母",古人还有另一条思路:不改变字母本身,而是改变字母的排列顺序。
一个简单的例子:把原文排成一个矩阵,按列写入,按行读出。比如原文 HELLO WORLD,写进一个 4×3 的矩阵:
H E L L
O W O
R L D按列写出:HOR E L LLO W D——字母一个没变,但顺序全乱了。这就是置换加密。
置换加密的好处是——频率分析不管用了,因为字母频率没有变。E 还是出现那么多次,它的统计特征没有被抹掉。但它的弱点同样明显:一旦攻击者知道加密方式是"打乱顺序",就可以用字频、词频、常见字母组合来推测排列规则。
考考你:这三种加密各有什么破绽
下面有三段密文,分别用凯撒、维吉尼亚和置换加密。你先猜一猜各自最有效的破解方法,然后把鼠标移到模糊文字上看看答案。
1. 凯撒密文: QHYHU JLYH XS
破解思路:暴力穷举 25 次即可。试到偏移 3,还原为 NEVER GIVE UP。
2. 维吉尼亚密文(关键词 4 个字母): LXFOPVEFRNHR
破解思路:第一步,用卡西斯基测试推测关键词长度为 4。第二步,把密文按位置分成 4 组,每组单独做频率分析。各组最常见字母分别对应 E,反推出关键词为 CODE。还原明文:ATTACKATDAWN。
3. 置换密文(4列矩阵,按列读出): TIKA AET TCSA TACK
破解思路:4 列矩阵意味着原文每 4 个字母一组。试着重排:第 1 列是 T、A、T、T,第 2 列是 I、E、C、A……还原矩阵按行读得到 TAKE ATTACK STAT。
一眼看清:古典密码是怎么一步步走到现代密码的
下面这张图把密码学两千年的演进压缩成一条线。注意观察每一个转折点发生了什么——每一次"被破解"都催生了下一代更强的方案。
古典密码给现代密码学上了三堂课
古典密码和现代密码之间的差距,不是"做得更好"的问题,而是整个安全哲学根本不同。古典密码暴露出来的问题,恰好帮我们总结了现代密码的三条铁律:
第一,安全不能依赖"规则保密"。 凯撒依赖攻击者不知道偏移量,维吉尼亚依赖攻击者不知道关键词。但历史反复证明——规则迟早会泄露。现代密码的做法刚好相反:算法全公开,只靠密钥保密。全世界都知道 AES 的算法细节,但没有密钥你照样解不开。这个观念的转变,是密码学从"手艺"变成"科学"的分水岭。
第二,密钥空间必须大到让穷举不可能。 凯撒的密钥空间是 25,维吉尼亚强一点但关键词通常是人能记住的单词,同样在字典攻击面前不堪一击。AES-256 的密钥空间是 2 的 256 次方——这个数字大到什么程度?即使你用全世界所有计算机一起算,算到太阳熄灭也算不完。
第三,密码分析手段会不断进步。 频率分析在九世纪就让简单替代失效了;卡西斯基测试在十九世纪让维吉尼亚失效了;差分密码分析和线性密码分析在二十世纪九十年代让 DES 变得不再可靠。昨天安全的算法,今天不一定安全。所以密码学不是"学一个算法管一辈子",而是持续评估、持续升级的过程。
理解了这三堂课,你再看现代密码学把算法公开、把安全建立在密钥和算力假设上——就一点都不奇怪了。
五、三大类算法,各管一摊
有了前面的铺垫,我们终于可以给现代密码算法分类了。主要三大类:
第一类: 对称加密
加密和解密用的是同一把密钥。你用它加密,接收方也用它解密。
- 优点:速度快,适合加密大量数据。AES 加密一个 GB 的文件可能就是几十毫秒的事。
- 难题:你怎么把密钥安全地交给接收方?如果是面对面见面给,那还算安全;如果要通过网络传,在没有加密通道的情况下传密钥,这不就是"先有鸡还是先有蛋"的问题吗?
- 代表算法:AES(现代主流)、3DES(历史过渡)、DES(已淘汰)
对称加密还有一个概念叫"分组加密模式"——算法像发动机,模式像变速箱。同样是 AES,选 ECB 模式和选 GCM 模式,安全性完全不同。ECB 模式下,相同的明文块会产生相同的密文块,就像把原文的"图案"透到了密文上——攻击者可以看到哪些块是重复的。GCM 模式不仅加密,还做认证,能同时保证机密性和完整性。今天新设计的系统,应该优先选择 GCM 这类 AEAD 模式。
直观对比:为什么 ECB 模式不安全
想象你要加密一张黑白照片。用 ECB 模式加密后,虽然像素值全变了,但明暗分块的结构依然可见——因为相同颜色的像素块加密后仍然是相同的密文块,攻击者"看"不出原文但能"看"出轮廓。
用一张经典的"Linux 企鹅 Tux"加密实验来理解:
这就是为什么"知道 AES 算法"和"能用好 AES"是两回事——模式选择和安全参数配置同样决定生死。
第二类: 非对称加密
有两把不同的密钥:公钥和私钥。公钥可以到处发,私钥必须死死守住。用公钥加密的数据,只能用对应的私钥解密。
- 解决的问题:就是前面说的"怎么在没见过的双方之间安全传递密钥"。你不用和接收方提前商量什么,你只需要拿到他的公钥,用公钥加密,就只有他的私钥能解。
- 缺点:计算量大,速度慢。RSA 加密几 KB 的数据可能没问题,加密几个 GB 的文件就太慢了。
- 实际用法:在真实的 HTTPS、PGP、VPN 里,非对称加密通常只用来做两件事:一是协商会话密钥(让双方安全地拿到一把临时的对称密钥),二是做数字签名(证明身份)。大量数据的加密工作,最终还是交给 AES 这类对称算法。
- 代表算法:RSA(最经典)、ECC(椭圆曲线,密钥更短但安全性相当)、Diffie-Hellman(密钥协商,本身不加密数据)。
第三类: 单向散列(Hash)
任意长度的数据输入,输出一个固定长度的"摘要"。这个过程是单向的——你不能从摘要反推出原文。
- 不是加密:很多人把 MD5、SHA-256 叫"加密算法",这是错的。加密必须能解密,Hash 不能解密。Hash 是用来"验指纹"的。
- 核心用处:判断文件有没有被改过。下载一个软件安装包,官网会同时给出 SHA-256 值。你下载完之后自己算一遍,如果和官网对不上,就说明文件可能在传输中损坏,或者被恶意替换了。
- 关键性质:雪崩效应——哪怕原文只改了一个字符,整个摘要会变得面目全非。这就让你能非常敏感地发现篡改。
- 代表算法:SHA-256、SHA-512(当前推荐)、SHA-1(已不安全)、MD5(已彻底淘汰)
- 重要提醒:普通 Hash 没有密钥,任何人都可以算。如果你的场景需要带密钥的完整性校验(比如验证 API 请求确实来自某个客户端),你应该用 HMAC,而不是裸 Hash。
实验一:Hash 摘要与雪崩效应——亲手验证"一字之差,天壤之别"
现在我们来做第一个实验。目标是让你亲眼看到,一份文件哪怕只改一个数字,SHA-256 摘要会发生多么剧烈的变化。这个实验不超过 5 分钟,但它建立起来的直觉,是你理解数字签名和完整性校验的基础。
先建一个测试文件,内容模拟了一个学生在课程平台上的信息:
# 创建临时实验目录,确保每次实验环境干净
LAB=/tmp/lesson13-crypto-lab
rm -rf "$LAB"
mkdir -p "$LAB"
# 生成测试文件,模拟课程平台上的用户信息
cat > "$LAB/plain.txt" <<'EOF'
学生姓名: test1
身份证后四位: 1234
课程平台登录口令: BGY@2026
EOF
# 分别用 MD5、SHA-1、SHA-256 计算摘要,对比不同算法的输出长度
md5sum "$LAB/plain.txt" # MD5:32字符,已不安全,仅作历史对比
sha1sum "$LAB/plain.txt" # SHA-1:40字符,也不建议新系统使用
sha256sum "$LAB/plain.txt" # SHA-256:64字符,当前主流的完整性校验算法
# 只修改文件中的一个字符(BGY@2026 → BGY@2027),观察摘要是否完全不同
sed 's/BGY@2026/BGY@2027/' "$LAB/plain.txt" > "$LAB/plain2.txt"
sha256sum "$LAB/plain.txt" "$LAB/plain2.txt" # 对比修改前后的 SHA-256 值真实输出:
74cd7061fcb88bbb774a8000e2bc9e25 /tmp/lesson13-crypto-lab/plain.txt
e3a8f035b8d8ffbbcc30a0ca87343bc024a78aa1 /tmp/lesson13-crypto-lab/plain.txt
bae6573411f7e538b4a168a6a8599e41858f63a79c78848574201a5eda29fd54 /tmp/lesson13-crypto-lab/plain.txt
bae6573411f7e538b4a168a6a8599e41858f63a79c78848574201a5eda29fd54 /tmp/lesson13-crypto-lab/plain.txt
545ec47d6504eeeade10cf71b5bb4db4139c757cbac5a5dafd323147ceff2708 /tmp/lesson13-crypto-lab/plain2.txt我们来一句一句读懂这个输出:
第一行是 MD5 的输出:74cd7061fcb88bbb774a8000e2bc9e25。它看起来随机,实际上就是你的文件内容经过 MD5 算出来的"指纹"。但我们不要用它来做安全判断——原因前面讲过,MD5 已经能被碰撞攻击了。
第二行是 SHA-1 的输出。比 MD5 长一点,但同样不建议新系统使用。
第三行是 SHA-256 的输出:bae65734...da29fd54。这是一串 64 个十六进制字符,对应 256 个比特。这是当前最常用的文件完整性校验算法。
重点看最后两行。我用 sed 只改了文件里的一个数字——把 BGY@2026 改成 BGY@2027,就一个字符的差别。结果呢?SHA-256 的值从 bae65...29fd54 变成了 545ec...ff2708,几乎每一个十六进制位都变了。
这就是雪崩效应:输入微小变化,输出剧烈变化。这个性质太有用了——你想验证一个文件有没有被人动过手脚,不用逐行比对内容,算一次 SHA-256,和原始值一对比就知道了。不相等?篡改无疑。相等?在目前算力下,可以认为文件完好无损。
这个实验对你有什么用: 以后你在网上下载任何软件、系统镜像、Docker 镜像,旁边都会附一个 SHA-256 校验值。你下载完之后应该养成习惯,本地算一遍比对一下。这不是多此一举——下载文件在传输中损坏、CDN 节点被投毒、镜像被替换、这些都不是理论假设,是真真切切发生过的事故。
🔬 互动实验:哈希与雪崩效应
尝试修改下方输入框里的任意一个字符(比如把“3点”改成“4点”),观察下方 SHA-256 哈希值的剧烈变化。 红色高亮部分代表与上一次相比发生改变的字符。
当前哈希长度:64 字符 (256 bits) | 变更字符数:0 / 64
实验二:AES 对称加密——把一份文件变成谁都看不懂的东西
Hash 能验指纹,但原文照样是明文摆在那里。如果文件内容本身就是敏感的,我们需要的是加密——把内容变成看不懂的密文。
AES 是目前世界上使用最广泛的对称加密算法。选它加密一个文件,就像把文件放进了保险箱,只有拿着正确密码的人才能打开。
# 用 AES-256-CBC 加密 plain.txt,输出密文 plain.aes
# -pbkdf2:从口令派生密钥,不直接拿人类密码当密钥
# -iter 120000:派生过程迭代 12 万次,大幅提高暴力破解成本
# -salt:加随机盐值,确保同一密码每次产生不同密文
openssl enc -aes-256-cbc -e \
-pbkdf2 -iter 120000 -salt \
-in "$LAB/plain.txt" \
-out "$LAB/plain.aes" \
-pass pass:"CourseKey#2026"
# 以十六进制查看密文前 64 字节,确认内容已变成随机字节流
# -An:不显示地址偏移列 -tx1:每字节用两位十六进制显示 -N 64:只看前 64 字节
od -An -tx1 -N 64 "$LAB/plain.aes"真实输出:
53 61 6c 74 65 64 5f 5f ba 09 2c 6a 40 61 5c 42
74 f1 e3 20 4c 78 f8 3f 7a 71 8a 13 56 e6 46 c7
96 a2 17 97 dd c7 30 94 a3 d5 36 7a 39 38 a5 9c
01 f9 d8 0a f3 35 10 7e 4c 43 ad 07 3a 3e 9b 90这个命令在做什么,我逐行解释:
openssl enc -aes-256-cbc -e 告诉 OpenSSL:"用 AES-256-CBC 模式,做加密(encrypt)"。enc 是 encode/encrypt 的意思,-e 明确是加密方向。等一下解密的时候,这里会换成 -d。
-pbkdf2 -iter 120000 这两个参数太容易被新手忽略了,但它们才是真正体现工程素养的地方。你输入的密码 CourseKey#2026 是一个人类能记住的字符串,但人类密码通常不够长、不够随机,直接当密钥用是危险的。-pbkdf2 的意思是:不要直接把我的密码当密钥,先用 PBKDF2 算法把它"拉伸"成一把真正的密钥。-iter 120000 的意思是:这个拉伸过程重复 12 万次。多一次迭代,攻击者暴力破解的时候就多花一点时间——12 万次迭代意味着你可以把暴力破解的成本提高好几个数量级。
-salt 的意思是:在密码前面加一段随机数据再拿去派生密钥。为什么需要这个?假设没有 salt,两个不同用户恰好用了同一个密码 123456,那么他们加密同一个文件会得到完全相同的密文——这本身就是一种信息泄露。有了 salt,即使用同一个密码,每次加密的结果也不一样。注意输出开头那 8 个字节 53 61 6c 74 65 64 5f 5f,对应的 ASCII 就是 Salted__——OpenSSL 把 salt 写在了密文文件的开头。salt 不是秘密,它可以公开;它的作用是让"同一密码"不产生"同一密文"。
od -An -tx1 是以十六进制方式查看文件的原始字节。输出里每一行都是 16 个字节,全是乱码——这说明原文已经被成功加密。
解密:拿着同一把钥匙开锁
# 用正确口令解密,-d 表示 decrypt(解密方向),参数必须和加密时完全一致
openssl enc -aes-256-cbc -d \
-pbkdf2 -iter 120000 \
-in "$LAB/plain.aes" \
-out "$LAB/plain.dec.txt" \
-pass pass:"CourseKey#2026"
# cmp 逐字节对比原始文件和解密文件,-s 静默模式(不输出差异,只返回退出码)
# && 表示上一条命令成功(退出码 0)才执行 echo
cmp -s "$LAB/plain.txt" "$LAB/plain.dec.txt" \
&& echo "OK: AES decrypted content matches plaintext"
# 用错误口令尝试解密——预期得到 bad decrypt 错误
openssl enc -aes-256-cbc -d \
-pbkdf2 -iter 120000 \
-in "$LAB/plain.aes" \
-out "$LAB/wrong.txt" \
-pass pass:"WrongKey"真实输出:
OK: AES decrypted content matches plaintext
bad decrypt
40676C5C007D0000:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:../providers/implementations/ciphers/ciphercommon_block.c:107:
exit=1你看到了什么:
拿了正确密码 CourseKey#2026 解密,cmp 命令对比了原始文件和还原后的文件,报告"一致"——解密成功。拿了错误密码 WrongKey 去解密,OpenSSL 直接报了 bad decrypt——解密失败,拿到的输出是垃圾数据。
这个实验回答了一个你可能有的疑问:"如果我不知道密码,但我手里有密文文件,我能不能随便试试?"答案是你可以试,但试出来的结果是不可读的垃圾。而且错误密码触发的 bad decrypt 错误不是 AES 算法本身判断"密码错了",而是解密过程中发现填充数据不合规——AES 分组加密要求明文长度是 16 字节的整数倍,不足的部分会填充补齐。如果解密时用了错误密钥,解密出来的"填充"会是乱码,填充校验就会失败。
你对这个实验的理解程度,决定了你以后设计加密方案时能不能避开一个常见坑: 很多人知道了 openssl enc -aes-256-cbc 这条命令就觉得"我会加密了"。但如果你不懂 -pbkdf2 是干什么的、不知道为什么不建议用 ECB 模式、不了解为什么不加密同时也得做完整性校验——那你写出来的加密代码很可能只是"看起来像加密了",实际上漏洞百出。
顺便看一眼 3DES:历史是用来理解的,不是用来用的
# 用 3DES 加密同一份文件,仅为对比历史算法和新算法的差异
# des-ede3-cbc:3 轮 DES + CBC 模式,有效密钥 168 位但分组仍只有 64 位
openssl enc -des-ede3-cbc -e \
-pbkdf2 -iter 120000 -salt \
-in "$LAB/plain.txt" \
-out "$LAB/plain.3des" \
-pass pass:"CourseKey#2026"
# 对比三种文件大小:明文、AES 密文、3DES 密文——加密都会增加体积(头部+salt+填充)
wc -c "$LAB/plain.txt" "$LAB/plain.aes" "$LAB/plain.3des"真实输出:
80 /tmp/lesson13-crypto-lab/plain.txt
112 /tmp/lesson13-crypto-lab/plain.aes
104 /tmp/lesson13-crypto-lab/plain.3des
296 总计
OK: 3DES decrypted content matches plaintext我们保留这个 3DES 实验,不是为了让你以后用它,而是让你感受一下历史算法的尴尬:同样的明文,AES 密文 112 字节,3DES 密文 104 字节,都比原文大——多出来的字节是加密头、salt 和填充。DES 的有效密钥只有 56 位,相当于一个 7 个字符的 ASCII 密码的搜索空间——今天的 GPU 可以在几小时内穷举完。你在面试中如果被问到"DES 为什么被淘汰了",你至少能说出"56 位密钥空间太小"这句话,因为你亲眼见过它长什么样。
🔐 互动实验:AES 对称加密演示
在这个实验中,加密和解密使用的是同一把密钥。你可以尝试修改解密密钥,看看使用错误的密钥会发生什么。
步骤 1:加密 (明文 ➔ 密文)
⬇️ 数据在网络中传输 ⬇️
网络中抓取到的密文 (谁都看不懂)
⬇️ 接收方收到密文 ⬇️
步骤 2:解密 (密文 ➔ 明文)
实验三:RSA 非对称加密——不用提前商量,也能安全通信
现在我们触碰到密码学最优雅的设计之一:非对称加密。
对称加密有个天然的困境:双方必须在加密通信之前就持有同一把密钥。如果你们两个素不相识,你怎么把密钥安全地交给他?当面给?打电话念?发邮件?——如果你能安全地传密钥,那你是不是也能直接安全地传消息本身?这个问题在密码学上叫"密钥分发问题"。
非对称加密就是为解决这个问题而生的。它的思路非常巧妙:生成两把数学上关联的密钥,一把叫公钥(可以满世界发),一把叫私钥(绝对不离开你的手)。
公钥加密的东西,只有对应的私钥能解开。所以流程是这样的:你想给 Bob 发秘密消息,你从某个地方拿到 Bob 的公钥,用公钥加密消息,然后把密文发给 Bob。Bob 用自己的私钥解密。整个过程中,你们不需要提前见面、不需要共享任何秘密。
但要注意一个关键限制:RSA 不能加密大文件。
RSA 的密文长度受密钥长度限制。2048 位的 RSA 密钥,单次最多能加密的数据量大约是 256 字节减去填充的开销。你想加密一张几 MB 的图片?不可能直接用 RSA。
那真实系统是怎么做的呢?混合加密。用 RSA(或 ECDH)协商出一把临时的 AES 会话密钥,然后用 AES 去加密海量数据。非对称算法负责"安全的第一次见面",对称算法负责"高效的日常通信"。这也是为什么你有必要同时学两种算法——它们在实际工程中是一起用的。
✍️ 互动实验:数字签名与防篡改验证
数字签名结合了哈希算法和非对称加密。发送方先计算摘要,再用私钥签名。接收方用公钥验签,并重新计算摘要进行比对。
👩 发送方 (Alice 的电脑)
🗝️ 用 Alice 的 私钥 签名
➡️ 网络传输 ➡️
👿 传输中 (可尝试篡改)
截获的签名 (黑客无法伪造):
➡️ 抵达接收方 ➡️
👨 接收方 (任何人/Bob)
🔑 用 Alice 的 公钥 验签
动手生成一对 RSA 密钥
# 创建独立的 PKI 实验目录(避免和前一个实验的 crypto lab 混淆)
LAB=/tmp/lesson13-pki-lab
rm -rf "$LAB"
mkdir -p "$LAB"
# 生成 2048 位 RSA 私钥
# genpkey:通用私钥生成命令 -algorithm RSA:指定算法 -pkeyopt rsa_keygen_bits:2048:密钥长度
openssl genpkey -algorithm RSA \
-out "$LAB/private.pem" \
-pkeyopt rsa_keygen_bits:2048
# 从私钥中提取公钥,-pubout 表示输出公钥
openssl rsa -pubout \
-in "$LAB/private.pem" \
-out "$LAB/public.pem"
# 查看私钥结构(-text 可读形式,-noout 不输出密钥本身,sed 只看前几行)
openssl rsa -in "$LAB/private.pem" -text -noout | sed -n '1,8p'
# 查看公钥结构(-pubin 告诉 openssl 输入是公钥而非私钥)
openssl rsa -pubin -in "$LAB/public.pem" -text -noout | sed -n '1,5p'真实输出节选:
Private-Key: (2048 bit, 2 primes)
modulus:
00:b3:e8:63:a5:8e:40:63:5a:42:e6:fc:3b:5e:1a:
18:a2:df:e8:6d:c3:20:e7:17:14:31:a9:3d:58:40:
6d:46:c1:2a:15:80:24:90:22:99:ad:27:bc:6f:1c:
Public-Key: (2048 bit)
Modulus:
00:b3:e8:63:a5:8e:40:63:5a:42:e6:fc:3b:5e:1a:观察输出,你会发现公钥和私钥里有一个共同的字段叫 modulus(n 值)。这说明它们确实是一对——在数学上,私钥包含 n 和 d,公钥包含 n 和 e,它们共享同一个 n。但私钥里的 d 是你绝对不能交出去的东西,公钥里只有 e 和 n,攻击者要从 e 和 n 推算出 d 在计算上不可行(这就是大整数分解难题)。
深入:RSA 背后的数学(选读,不考试)
RSA 的安全性建立在"大整数分解"这个数学难题上。下面是三个核心公式,每个只用到乘方和取余——但组合起来就是一套完整的非对称加密。
公式一:密钥生成
e×d≡1(mod(p−1)(q−1))
各符号含义:
- p 和 q:随机选的两个超级大质数,生成密钥时用一次,用完可以销毁。
- n=p×q:p 和 q 的乘积,这个是公开的——公钥和私钥里都有它。
- e:公开指数,通常固定为 65537(二进制几乎全是 0,计算效率高)。
- d:私钥指数,这个绝不能公开,从 e、p、q 反算出来。
公式在说什么:e 乘以 d,除以 (p−1)(q−1),余数等于 1。也就是说 d 是 e 在模 (p−1)(q−1) 下的"倒数"。算出 d 需要知道 (p−1)(q−1),而要知道这个就必须知道 p 和 q。攻击者手里只有 n,拆不出 p 和 q 就算不出 d——这就是 RSA 安全性的数学根基。
公式二:加密(用公钥 (n,e))
C=Memodn
各符号含义:
- M:明文,在计算机里就是一个大整数(文字→ASCII→拼起来)。
- C:密文。"模 n"限制了结果小于 n,所以密文长度最多和 n 一样大——2048 位 RSA 加密结果固定为 256 字节。
公式在说什么:把明文做 e 次方,除以 n,取余数。这个余数就是密文。谁都能做,因为 e 和 n 都在公钥里。
公式三:解密(用私钥 (n,d))
M=Cdmodn
公式在说什么:把密文做 d 次方,除以 n,取余数——结果恰好等于原文 M。只有持有私钥 d 的人能做到。因为 e 和 d 有前面的模逆关系,数学上可以严格证明 Cdmodn=M——两次乘方取余恰好抵消。
直观理解:加密是"垫高一次再取余",解密是"再垫高一次再取余",两次操作互相抵消回到原点。但只有拿着正确的 d 才能抵消,拿错了就回到随机乱码。
关键点:知道 n 是 p×q 很容易,但从 n 反推出 p 和 q 在计算上极其困难——当 n 有 2048 位时,这个分解需要的计算量超出当前人类全部算力的总和。但如果有一天量子计算机成熟,Shor 算法就能在多项式时间内分解大整数——RSA 就会彻底失效。这也是密码学必须持续演进的又一个证明。
⚛️ 互动实验:RSA 大指数加密与量子降维打击
RSA 的安全基石是大整数分解难题。点击下一步,对比观察传统超级计算机与未来量子计算机面对 2048 位 RSA 密钥时的绝望与震撼。
1
2
3
4
5
6
🛡️ 防御方 (企业服务器)
1. 生成 2048 位超大模数 (N = p × q)
N = 83a7f9c2... (省略 600 位) ...3b1d5e9f
2. 使用公钥大指数加密数据
明文:绝密战略企划.pdf
密文:0x9F82A1B...
密文:0x9F82A1B...
⬇️ 密文被黑客截获 ⬇️
💻 传统黑客 (硅基超级计算机)
尝试暴力分解大整数 N...
算力:每秒运算 100 亿亿次
当前进度:0.00000001%
预计耗时:3000 亿年 (宇宙热寂)
当前进度:0.00000001%
预计耗时:3000 亿年 (宇宙热寂)
🔮 未来黑客 (量子计算机)
运行 Shor 算法 (寻找周期 r)...
算力:4096 物理量子比特
预计耗时:正在坍缩...
预计耗时:正在坍缩...
🚨 密码防线被彻底击穿
由于 N 被成功拆解成 p 和 q,量子黑客瞬间推导出了 私钥 d。
解密成功,获取明文:绝密战略企划.pdf
解密成功,获取明文:绝密战略企划.pdf
第 1 步:企业生成了 2048 位的 RSA 密钥对。这里的模数 N 是由两个 1024 位的极大素数 (p 和 q) 相乘得到的。乘法很简单,但逆向拆解极难。
用公钥加密、用私钥解密
# 创建一条机密消息——模拟 Alice 想秘密发给 Bob 的内容
echo 'Alice发给Bob的机密消息: 只允许Bob读取' > "$LAB/message.txt"
# 用 Bob 的公钥加密消息(-pubin 告诉 openssl -inkey 指定的是公钥而非私钥)
# pkeyutl:低级公钥操作命令,直接执行 RSA 加密
openssl pkeyutl -encrypt \
-inkey "$LAB/public.pem" -pubin \
-in "$LAB/message.txt" \
-out "$LAB/message.rsa"
# 对比明文和密文的文件大小——50 字节明文膨胀到 256 字节密文
wc -c "$LAB/message.txt" "$LAB/message.rsa"
# 用 Bob 的私钥解密——只有私钥持有者能还原原文
openssl pkeyutl -decrypt \
-inkey "$LAB/private.pem" \
-in "$LAB/message.rsa" \
-out "$LAB/message.dec.txt"
# 查看解密结果,确认和原文一致
cat "$LAB/message.dec.txt"真实输出:
50 /tmp/lesson13-pki-lab/message.txt
256 /tmp/lesson13-pki-lab/message.rsa
306 总计
Alice发给Bob的机密消息: 只允许Bob读取原文 50 字节,加密后 256 字节——这就是前面说的 RSA 密文膨胀。50 字节的消息就膨胀到了 256 字节,如果是 50MB 的文件,那效率没法看。但这个实验的价值在于,你亲身体验了"拿到公钥就能加密,但没有私钥就是解不开"这个过程。
💻 互动实验:SSH 连接中的 RSA 真实应用全流程
真实世界中,RSA 太慢,无法用来加密海量数据。在 SSH 登录时,RSA 的唯一任务就是:帮助客户端和服务器安全地协商出一把“对称密钥(AES Session Key)”。后续的所有命令行操作,都用这把 AES 密钥来加密。
1
2
3
4
5
6
7
💻 SSH 客户端 (你的电脑)
🔑 缓存的服务器公钥
ssh-rsa AAAAB3Nza...
⚡ 生成的临时会话密钥 (AES)
SessionKey_9982AB
📦 被公钥加密的会话密钥
ENC(SessionKey_9982AB, PubKey)
互联网 (不安全)
⬅️ 明文发送公钥 ⬅️
ssh-rsa AAAAB3Nza...
➡️ 发送被加密的临时密钥 ➡️
ENC(SessionKey, PubKey)
🛡️ AES 加密隧道已建立 🛡️
双向加密通信 (ls, cd, cat...)
🖥️ SSH 服务器 (目标主机)
🗝️ 服务器私钥 (绝不泄露)
-----BEGIN PRIVATE KEY-----...
🔑 服务器公钥 (可公开)
ssh-rsa AAAAB3Nza...
⚡ 解密得到的临时会话密钥
SessionKey_9982AB
当前步骤 0:等待发起 SSH 连接请求...
实验四:数字签名——不是"证明我没偷看",而是"证明是我发的,而且没被改过"
很多人会把加密和签名搞混。其实它们的方向恰好相反。
- 加密:你用对方的公钥加密,只有对方私钥能解密。这是在回答"谁能看"。
- 签名:你用你自己的私钥签名,任何人拿你的公钥都能验证。这是在回答"谁发的、有没有被改"。
为什么签名不是"拿私钥把整篇文章加密"呢?你想,如果文章有一万字,你用 RSA 私钥去"加密"一万字,速度慢不说,密文还会膨胀得不像样。工程上的正确做法是:先算摘要,再对摘要签名。
流程是这样的:
- 发送者写出原文。
- 发送者对原文算 SHA-256 摘要。
- 发送者用自己的私钥对这个摘要做签名运算,生成一个"签名文件"。
- 发送者把原文和签名文件一起发给接收者。
- 接收者拿到原文和签名后,做两件事:第一,自己对原文重新算一次 SHA-256;第二,用发送者的公钥去验证签名文件。如果验签通过,且自己算的摘要和签名里隐含的摘要一致——那就两件事同时被证明了:原文没被改、确实是那个私钥持有者签发的。
✍️ 互动实验:数字签名全流程推演 (Step-by-Step)
在这个实验中,我们将以极慢的镜头回放数字签名的全过程。重点观察:为什么是先算哈希?为什么一定要用私钥签名?接收方是怎么识破黑客篡改的?
1
2
3
4
5
6
7
8
👩 发送方 (Alice)
⬇️ 1. 提取 SHA-256 摘要 ⬇️
🏷️ Hash A (原文指纹)
⬇️ 2. 用 Alice 的私钥加密摘要 ⬇️
🗝️ Alice 的私钥 + Hash A = 数字签名
SIG_ALICE(...)
🌐 互联网 (不安全)
➡️ 发送数据包 ➡️
📦 截获的数据包
附带签名: SIG_ALICE(...)
➡️ 抵达目的地 ➡️
👨 接收方 (Bob)
📥 收到的合同文件:
⬇️ 3. 自己算摘要
🏷️ Hash B
⬇️ 4. 公钥解密签名
🏷️ 解密出的 Hash A
⬇️ 5. 终极比对 ⬇️
第 1 步:Alice 准备发送一张电子欠条。为了防止金额被改,或者事后赖账,她决定使用数字签名。
动手验证签名与篡改检测
# 用私钥对文件签名:dgst 先算 SHA-256 摘要,再用 -sign 私钥对摘要签名
# 输出 message.sig 是签名文件(不是加密的原文)
openssl dgst -sha256 -sign "$LAB/private.pem" \
-out "$LAB/message.sig" \
"$LAB/message.txt"
# 用公钥验证签名:重新计算文件摘要,和签名匹配则通过
# -verify 指定公钥 -signature 指定签名文件 最后参数是被验证的文件
openssl dgst -sha256 -verify "$LAB/public.pem" \
-signature "$LAB/message.sig" \
"$LAB/message.txt"
# 构造一份被篡改的文件:复制原文,追加一行新内容
cp "$LAB/message.txt" "$LAB/message.tampered.txt"
echo '追加一行篡改内容' >> "$LAB/message.tampered.txt"
# 用同一份签名验证被篡改的文件——预期 Verification failure
openssl dgst -sha256 -verify "$LAB/public.pem" \
-signature "$LAB/message.sig" \
"$LAB/message.tampered.txt"真实输出:
Verified OK
40B76DC99B790000:error:02000068:rsa routines:ossl_rsa_verify:bad signature:../crypto/rsa/rsa_sign.c:441:
40B76DC99B790000:error:1C880004:Provider routines:rsa_verify_directly:RSA lib:providers/implementations/signature/rsa_sig.c:1038:
Verification failure
exit=1第一次验证:Verified OK——签名、原文、公钥三者匹配。原文是 Alice 写的,签名是 Alice 用私钥生成的,你拿 Alice 的公钥验证通过了,你就知道:这条消息确实是 Alice 发的,内容没有被改过。
第二次验证:我在原文末尾追加了一行篡改内容,重新验签——Verification failure。因为原文变了,SHA-256 摘要变了,和签名里记录的原始摘要对不上了。
回头想想你在这门课前面学的文件上传漏洞、中间人攻击——如果那些攻击修改了传输中的数据,数字签名就是能让接收方发现"这份数据已经不可靠了"。数字签名不阻止攻击发生,但它让你知道攻击发生了——这就是"可检测的安全"。
实验五:PGP——把前面学的所有技术串起来
PGP(Pretty Good Privacy)是一个集大成者。一份 PGP 加密的邮件,实际上同时用了:
- 随机生成一把一次性 AES 会话密钥,用来加密邮件正文(因为它快)
- 用收件人的 RSA 公钥加密这把会话密钥(因为直接用 AES 传密钥不安全)
- 用发件人的 RSA 私钥对邮件做数字签名(因为要证明"是我发的")
换句话说,PGP 就是前面实验一、二、三、四的组合拳。你学会了 PGP 的工作方式,就等于把对称加密、非对称加密、Hash 和数字签名串联成了一个完整的应用场景。
下面这张图把混合加密的全过程画出来了。注意消息体(大流量)走 AES,会话密钥(短小)走 RSA——各用各的长处,谁也不拖谁的后腿。
现在动手:
# 设置独立的 GNUPGHOME,避免污染本机真实 GPG 密钥库
GNUPGHOME=/tmp/lesson13-gnupg
rm -rf "$GNUPGHOME"
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME" # GPG 要求密钥目录权限为 700(只有 owner 可读写执行)
export GNUPGHOME
# 批量生成 test1 的 RSA 密钥对(公钥+私钥)
# --batch:非交互模式 %no-protection:私钥不加密码(仅课堂实验,真实环境必须设密码)
gpg --batch --generate-key <<'EOF'
Key-Type: RSA
Key-Length: 2048
Subkey-Type: RSA
Subkey-Length: 2048
Name-Real: test1
Name-Email: test1pgp@example.com
Expire-Date: 0 # 0 表示永不过期(仅实验用)
%no-protection
%commit
EOF
# 批量生成 test2 的密钥对
gpg --batch --generate-key <<'EOF'
Key-Type: RSA
Key-Length: 2048
Subkey-Type: RSA
Subkey-Length: 2048
Name-Real: test2
Name-Email: test2pgp@example.com
Expire-Date: 0
%no-protection
%commit
EOF
# 列出已生成的密钥 UID,确认 test1 和 test2 都在
gpg --list-keys --with-colons | awk -F: '/^uid:/ {print $10}'真实输出:
test1 <test1pgp@example.com>
test2 <test2pgp@example.com>注意这里我们设置了独立的 GNUPGHOME 目录,避免和你本机真实的 GPG 密钥混在一起。课堂密钥用 %no-protection 不加密码保护,只是为了脚本能自动跑完——真实使用中,你的私钥一定要设密码。
# 创建测试消息:test1 发给 test2
echo 'PGP测试: test1发送给test2的课程通知' > "$LAB/pgp-note.txt"
# 用 test2 的公钥加密文件(只有 test2 的私钥能解开)
# --recipient:指定收件人(gpg 自动查找其公钥)
# --trust-model always:跳过信任检查(实验用,真实环境需要验证公钥指纹)
gpg --batch --yes --trust-model always \
--encrypt --recipient test2pgp@example.com \
--output "$LAB/pgp-note.gpg" \
"$LAB/pgp-note.txt"
# 十六进制查看 PGP 密文前 48 字节——全是乱码
od -An -tx1 -N 48 "$LAB/pgp-note.gpg"
# 用 test2 的私钥解密(gpg 自动匹配收件人对应的私钥)
gpg --batch --yes --decrypt \
--output "$LAB/pgp-note.dec.txt" \
"$LAB/pgp-note.gpg"
# 查看还原后的原文
cat "$LAB/pgp-note.dec.txt"真实输出:
85 01 0c 03 70 d2 b2 28 44 b0 1d 36 01 07 ff 47
aa 39 7e 03 28 da df 66 6b 90 cd 5c 9e 7a cb fc
b6 43 7c 9c c9 ba ed a9 6c b8 4d 29 f5 09 77 ef
gpg: 由 rsa2048 密钥加密,标识为 70D2B22844B01D36,生成于 2026-05-18
"test2 <test2pgp@example.com>"
PGP测试: test1发送给test2的课程通知od 输出的前 48 个字节全是乱码——密文形态。但 GPG 在解密时能自动识别这包数据是用什么算法加密的、应该用哪个私钥解密。最后一行清楚地还原了原文。
如果这个实验让你觉得"好像也没什么神奇的",那恰恰说明 PGP 的用户体验做得好——它把复杂的密码运算都封装掉了。但这不意味着你可以不懂它的原理。将来你如果需要在公司内部部署 PGP 邮件加密方案,你就得回答这些问题:密钥在哪里生成?私钥怎么备份?离职员工的公钥什么时候从信任列表移出?证书过期了谁来续?——这些不是 GPG 命令能替你回答的。
🛡️ 互动实验:PGP 混合加密全流程解构
PGP 把前面学的所有技术串了起来:用 AES 加密内容(快),用 RSA 公钥加密 AES 密钥(安全传钥),用 RSA 私钥签名(证明身份)。点击“下一步”观察这套精密的“俄罗斯套娃”机制。
1
2
3
4
5
6
7
8
9
👩 发送方 (Alice 的电脑)
📝 1. 原始邮件正文
✍️ 2. Alice 用私钥签名 (防篡改)
SIG_ALICE(Hash(正文))
⚡ 3. 随机生成一次性 AES 会话密钥
🔑 AES_KEY_889F
📦 4. AES 加密 (正文 + 签名)
AES_ENC(正文 + 签名)
🔐 5. Bob 公钥加密 AES 密钥
RSA_ENC(🔑 AES_KEY_889F, Bob公钥)
🌐 互联网 (抓包区)
➡️ 发送 PGP 数据包 ➡️
✉️ 截获的 PGP 密文
密文密钥: RSA_ENC(...)
密文正文: AES_ENC(...)
➡️ 抵达接收方 ➡️
👨 接收方 (Bob 的电脑)
🗝️ 6. Bob 私钥解密 AES 密钥
🔑 恢复出: AES_KEY_889F
🔓 7. AES 解密正文与签名
恢复出: 明文正文 + SIG_ALICE
📝 8. 读取正文
PGP测试: test1发送给test2的机密通知
🔍 9. Alice 公钥验签
✅ 验签通过:确实是 Alice 发的,未被篡改
第 1 步:Alice 写好了一封机密邮件,准备通过 PGP 发送给 Bob。
实验六:PKI 证书链——解决"这把公钥到底是谁的"
最后一个问题,也是整个信任体系的地基。
前面 RSA 实验里,你拿到了 Bob 的公钥,你用它加密了一条消息。但你怎么确定这把公钥真的是 Bob 的?如果中间有个攻击者,在你和 Bob 之间截获了通信,他把他自己的公钥发给你、说"这是 Bob 的公钥",你有什么办法识破?
这就是 PKI(公钥基础设施)要解决的问题。PKI 引入了一个你信任的第三方——CA(证书颁发机构)。它的逻辑是这样的:
- 你信任 CA A(因为操作系统或浏览器出厂的时候就预装了 A 的根证书)。
- Bob 去找 CA A 申请证书:CA A 验证了 Bob 确实是他自称的那个人之后,用自己的私钥给 Bob 的公钥"签了个名"——这就是"颁发证书"。
- Bob 把证书给你看。你一看,签发者是 CA A,你用你系统里预装的 CA A 公钥验证这张证书的签名——验证通过了。
- 既然你信任 CA A,CA A 信任 Bob,那你可以信任证书里的公钥确实是 Bob 的。
这个"信任链"模型就是 PKI 的核心。你在浏览器里访问 HTTPS 网站时看到的那个小锁图标,背后就是这套机制在工作:浏览器用内置的根证书,顺着证书链一层一层验上去,最后确认这个网站的公钥是有效的。
自己搭建一条 PKI 信任链
###############################################################################
# 第 1 步:生成自签名根 CA 证书
# req -x509:直接生成自签名证书(不是证书请求)
# -nodes:私钥不加密码(仅实验)
# -days 365:有效期 1 年
# -subj '/CN=...':证书主题,CN=Common Name
# -keyout ca.key:CA 私钥(信任根,必须保密)
# -out ca.crt:CA 根证书(可公开分发)
###############################################################################
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj '/CN=BGY Lesson13 Root CA' \
-keyout "$LAB/ca.key" \
-out "$LAB/ca.crt"
###############################################################################
# 第 2 步:为服务器生成私钥和证书签名请求(CSR)
# CSR 包含服务器公钥和身份信息,等待 CA 签名
###############################################################################
openssl req -newkey rsa:2048 -nodes \
-subj '/CN=lesson13.local' \
-keyout "$LAB/server.key" \
-out "$LAB/server.csr"
###############################################################################
# 第 3 步:CA 用自己的私钥签发服务器证书
# -CA ca.crt:指定 CA 证书 -CAkey ca.key:指定 CA 私钥
# -CAcreateserial:生成序列号文件 -days 90:证书有效期 90 天
###############################################################################
openssl x509 -req \
-in "$LAB/server.csr" \
-CA "$LAB/ca.crt" \
-CAkey "$LAB/ca.key" \
-CAcreateserial \
-days 90 \
-out "$LAB/server.crt"
###############################################################################
# 第 4 步:验证证书链——从服务器证书回溯到根 CA,预期输出 OK
###############################################################################
openssl verify -CAfile "$LAB/ca.crt" "$LAB/server.crt"
###############################################################################
# 第 5 步:查看证书详情
# -noout:不输出证书本身 -subject:持有者 -issuer:签发者
###############################################################################
openssl x509 -in "$LAB/server.crt" -noout -subject -issuer -startdate -enddate真实输出:
/tmp/lesson13-pki-lab/server.crt: OK
subject=CN=lesson13.local
issuer=CN=BGY Lesson13 Root CA
notBefore=May 18 08:34:09 2026 GMT
notAfter=Aug 16 08:34:09 2026 GMT这个实验你只用了 5 条命令,就搭建了一条完整的信任链:CA 自签了根证书,服务器生成了私钥和证书请求,CA 用自己私钥签发了服务器证书。openssl verify 返回 OK,证明服务器证书确实可以回溯到我们信任的 CA。
输出里的关键信息:
subject=CN=lesson13.local:证书的持有者是lesson13.local。issuer=CN=BGY Lesson13 Root CA:签发者是我们的实验 CA。notBefore和notAfter:证书的有效期。过期了浏览器就会报"不安全"。
这个实验直接回答了"为什么浏览器能判断一个网站安不安全"——因为浏览器的根证书库里没有你这个自签 CA,所以用你自签 CA 签发的证书去部署 HTTPS 网站,别人的浏览器打开会报警。但如果你在企业的 Windows 域控里用组策略把你企业 CA 的根证书推送到所有员工的电脑上,那员工访问你内部网站的 HTTPS 时就不会报警了。这就是企业内网 PKI 的实际玩法。
本课复盘:你学完这一课应该能说清楚这些事
学完这一课,如果有人问你"密码学到底是干嘛的",你不应该再背诵算法名称了。你应该能用自己的话,从"四个安全需求"出发,把实验里的六个实验串成一条线讲出来:
- AES 解决了什么问题?文件内容保密。为什么需要
-pbkdf2 -iter -salt?因为人类密码不能直接当密钥用。 - RSA 解决了什么问题?不用提前见面,也能安全通信。为什么不能直接加密大文件?因为密文长度等于密钥长度。
- Hash 解决了什么问题?验指纹,发现篡改。为什么 Hash 不是加密?因为不能解密。
- 数字签名解决了什么问题?证明"是我发的、没被改"。为什么是先摘要再签名?因为直接签全文太慢了。
- PGP 解决了什么问题?把上面所有技术打包成一个端到端通信工具。
- PKI 解决了什么问题?确认"公钥的主人是谁"。为什么需要信任链?因为你不能凭空相信一把公钥。
课后任务
以下任务全部截图提交,每张截图需要能看到你输入的命令和终端输出结果。
- 截图 SHA-256 雪崩效应:修改
plain.txt中任意一个字符,运行sha256sum对修改前后的文件分别求值,截一张包含两次输出的图。 - 截图 AES 错误口令解密:用正确口令加密
plain.txt,再用错误口令尝试解密,截一张包含bad decrypt报错的图。 - 截图 RSA 密钥生成与加密:生成一对 RSA 密钥,用公钥加密一条短消息、私钥解密还原,截一张包含
cat解密结果的图。 - 截图 GPG 加密通信:生成 test1 和 test2 两对密钥,用 test2 的公钥加密一份文件,再用 test2 的私钥解密,截一张包含解密后原文的图。
- 截图 PKI 证书链验证:用自建 CA 签发一张服务器证书,运行
openssl verify和openssl x509查看 subject/issuer/有效期,截一张包含验证结果的图。
实验环境清理
# 删除三个实验产生的临时目录,释放磁盘空间
rm -rf /tmp/lesson13-crypto-lab # 实验一、二的 Hash 和 AES 实验文件
rm -rf /tmp/lesson13-pki-lab # 实验三、四、六的 RSA 和 PKI 实验文件
rm -rf /tmp/lesson13-gnupg # 实验五的 GPG 密钥库