面向网络安全从业者的密码学技术参考:原理、应用、攻击与防御、学习路线
目录
1. 密码学核心概念
1.1 对称加密 vs 非对称加密
对称加密:加密和解密使用同一个密钥。
非对称加密:加密和解密使用一对密钥(公钥 + 私钥)。
核心区别在于密钥分发问题:对称加密需要双方事先共享密钥,非对称加密解决了这个难题。

| 对比维度 | 对称加密 | 非对称加密 |
|---|---|---|
| 速度 | 快 (GB/s 级别) | 慢 (比对称慢 100-1000 倍) |
| 密钥长度 | AES-128 即可 | RSA-3072 / ECC-256 |
| 密钥分发 | ❌ 需要安全通道 | ✅ 公钥可公开 |
| 典型算法 | AES, ChaCha20, 3DES | RSA, ECC (ECDSA, ECDH), Ed25519 |
| 用途 | 大量数据加密 | 密钥交换、数字签名 |
💡 实际工作场景 : TLS 握手用 ECDH(非对称)协商出一个对称密钥,之后所有数据传输用 AES-GCM(对称)。这就是混合加密体系------用非对称解决密钥分发,用对称保证传输效率。
1.2 哈希 vs MAC vs 数字签名
三者的核心区别:
-
哈希 (Hash) : 保证完整性,无密钥,任何人都能验证
-
MAC (Message Authentication Code) : 保证完整性 + 认证,共享密钥,双方可互相验证
-
数字签名 : 保证完整性 + 认证 + 不可抵赖,非对称密钥,私钥签名,公钥验证

| 特性 | 哈希 | HMAC | 数字签名 |
|---|---|---|---|
| 完整性 | ✅ | ✅ | ✅ |
| 身份认证 | ❌ | ✅ | ✅ |
| 不可抵赖 | ❌ | ❌ | ✅ |
| 密钥 | 无 | 共享对称密钥 | 非对称密钥对 |
| 性能 | 极快 | 快 | 慢 |
| 典型场景 | 文件校验、密码哈希 | API 签名、TLS 完整性 | 代码签名、证书、JWT |
1.3 分组密码工作模式
块密码(如 AES)一次处理固定长度的数据块(AES 为 128 位/16 字节)。工作模式决定了如何处理多个块。
AES 工作模式对比图


工作模式选择建议:
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 一般加密需求 | AES-GCM | 认证加密,现代标准 |
| 高性能流式加密 | ChaCha20-Poly1305 | 无硬件 AES 加速时比 AES-GCM 快 |
| 加密数据库字段 | AES-SIV | 确定性加密,相同明文产生相同密文 |
| 遗留系统兼容 | AES-CBC + HMAC | 旧系统不支持 GCM 时的退选 |
| 绝不使用 | AES-ECB | 完全不安全 |
💡 应急响应场景: 发现勒索软件使用 AES-ECB 加密文件?这可能是一个线索------专业勒索软件通常用 RSA+ECB 加密密钥 + AES-CBC/GCM 加密文件。用 ECB 的可能是业余作品。
2. 主流算法深入解析
2.1 AES --- 高级加密标准
直觉理解:AES 在做什么?
AES 不是"数学上很难",而是"通过大量简单操作的组合产生极高复杂度"。把 AES-128 想象成一台搅面机:
原始面团(明文) ──► [10 轮搅面] ──► 完全混合的面团(密文)

密钥长度选择
| 算法 | 密钥长度 | 安全级别 | 轮数 | 适用场景 |
|---|---|---|---|---|
| AES-128 | 128 bit | 128 bit | 10 | 默认选择,量子计算机前安全 |
| AES-192 | 192 bit | 192 bit | 12 | 合规要求 (Suite B) |
| AES-256 | 256 bit | 256 bit | 14 | 绝密数据、后量子过渡期 |
💡 实战建议: 99% 的场景 AES-128-GCM 足够。AES-256 只比 AES-128 慢约 40%,但安全余量更大。如果你在做政府/军事项目,用 AES-256。否则,AES-128 足够用到量子计算机实用化之前。
为什么 GCM 是现代标准?
GCM = CTR 模式加密 + GMAC 认证。关键优势:
-
一次操作同时完成加密和认证(不需要先加密再 HMAC)
-
并行化:CTR 和 GHASH 都可以并行
-
额外认证数据 (AAD):可以认证不加密的头部信息(如协议版本号)
-
TLS 1.3 强制要求的 AEAD 模式之一
# Python cryptography 库 — AES-GCM 正确用法
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# 生成密钥(16 字节 = AES-128, 32 字节 = AES-256)
key = AESGCM.generate_key(bit_length=256) # 32 bytes
aesgcm = AESGCM(key)
# 生成 nonce(必须每次不同!96 位推荐)
nonce = os.urandom(12)
# 加密(AAD 是不加密但要认证的数据)
aad = b"version=1.0,type=confidential"
plaintext = b"这是一段机密信息"
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 解密(nonce 和 aad 必须与加密时相同)
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
# ⚠ 如果密文被篡改,decrypt() 会抛出 InvalidTag 异常
常见错误用法
# ❌ 错误 1: 硬编码 IV/Nonce
key = b"0123456789abcdef" * 2 # 硬编码密钥
iv = b"\x00" * 16 # 固定 IV = 灾难!
# ❌ 错误 2: ECB 模式(默认就是 ECB!)
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
cipher = Cipher(algorithms.AES(key), mode=None) # 无 mode = ECB!
# ❌ 错误 3: 重用 Nonce(GCM 模式下 = 密钥泄露)
nonce = os.urandom(12)
c1 = aesgcm.encrypt(nonce, msg1, None)
c2 = aesgcm.encrypt(nonce, msg2, None) # Nonce 重用!攻击者可恢复明文!
# ✅ 正确做法
nonce = os.urandom(12) # 每次加密生成新的 nonce
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 存储格式: nonce(12) + ciphertext + tag(16)
2.2 RSA --- 非对称加密基石
密钥生成过程(直觉理解)
1. 随机选两个大质数 p 和 q(各 1024 位 → RSA-2048)
2. 计算 n = p × q (n 是公钥的一部分,2048 位)
3. 计算 φ(n) = (p-1) × (q-1)
4. 选择 e = 65537(常用,质数且二进制只有两个 1,计算快)
5. 计算 d = e⁻¹ mod φ(n) (d 是私钥)
公钥 = (n, e)
私钥 = (n, d)
加密: c = m^e mod n
解密: m = c^d mod n
安全性基础 :从 n 反推 p 和 q 是整数分解问题,目前没有已知的多项式时间算法。但 Shor 算法在量子计算机上可以高效分解,所以 RSA 在后量子时代不安全。
为什么 RSA-2048 是底线?
| 密钥长度 | 安全级别 | 分解难度 (GNFS) | 建议 |
|---|---|---|---|
| RSA-1024 | ~80 bit | 已可分解(2020 年分解 795 位) | ❌ 已不安全 |
| RSA-2048 | ~112 bit | 当前算力需要数百万年 | ✅ 最低要求 |
| RSA-3072 | ~128 bit | 远超出当前算力 | ✅ 推荐 |
| RSA-4096 | ~140 bit | 极其困难 | 过度保护,性能差 |
💡 合规检查: PCI-DSS 要求 RSA ≥ 2048。NIST SP 800-131A 建议 2023 年后使用 RSA-3072 或 ECC。
OAEP 填充 vs PKCS#1 v1.5
from cryptography.hazmat.primitives.asymmetric import rsa, padding, hashes
from cryptography.hazmat.primitives import serialization
# 生成 RSA-2048 密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
# ❌ PKCS#1 v1.5 — 已被 Bleichenbacher 攻击利用
# 不推荐使用,但很多遗留系统仍在使用
# ✅ OAEP — 推荐
from cryptography.hazmat.primitives.asymmetric import padding
message = b"机密数据"
ciphertext = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
为什么 OAEP 更安全?
PKCS#1 v1.5 的填充格式是 0x00 || 0x02 || 随机非零字节 || 0x00 || 明文。Bleichenbacher 攻击利用服务器对填充错误的不同响应(padding oracle)逐步恢复明文。
OAEP 使用随机数 + 哈希函数的 Feistel 网络结构,即使攻击者知道填充格式错误,也无法获得任何有用信息。
2.3 ECC --- 椭圆曲线密码学
直觉理解
RSA 的安全性基于大整数分解 ,ECC 的安全性基于椭圆曲线离散对数问题 (ECDLP)。
椭圆曲线方程: y² = x³ + ax + b (mod p)
在曲线上选一个基点 G,选一个私钥 d,
公钥 Q = d × G (曲线上的点乘)
已知 Q 和 G 求 d → 极难 (ECDLP)
已知 d 和 G 求 Q → 容易 (点乘)
核心优势:更短的密钥 = 同等安全
| 安全级别 | RSA 密钥 | ECC 密钥 | 比例 |
|---|---|---|---|
| 128 bit | 3072 位 | 256 位 | 12:1 |
| 256 bit | 15360 位 | 512 位 | 30:1 |
更短的密钥 = 更快的运算 + 更少的带宽 + 更小的存储。这就是为什么 TLS 1.3、SSH、加密货币都选择了 ECC。

常用曲线
| 曲线 | 用途 | 安全性 | 备注 |
|---|---|---|---|
| P-256 (secp256r1) | TLS、JWT | 128 bit | NIST 标准,广泛支持 |
| P-384 | 政府/军事 | 192 bit | NIST Suite B |
| Curve25519 | SSH、TLS 1.3 | 128 bit | Bernstein 设计,推荐首选 |
| Ed25519 | SSH 密钥、签名 | 128 bit | 基于 Curve25519,签名比 ECDSA 更快更安全 |
💡 实战建议 : 新系统一律选 Curve25519/Ed25519。它比 NIST 曲线更快、更安全(没有潜在后门嫌疑)、实现更简单。OpenSSH 8.2+ 默认使用 Ed25519。
2.4 SHA-2 / SHA-3 与 HMAC
为什么 SHA-1 已死?
2017 年 Google 的 SHAttered 攻击首次实现了 SHA-1 的实际碰撞。此后碰撞成本已降至约 $45,000。
SHA-1 碰撞意味着:
攻击者可以构造两个不同的文件,它们有相同的 SHA-1 哈希值
实际影响:
- Git 用 SHA-1 做对象校验 → 已迁移到 SHA-256
- X.509 证书签名 → 已禁用 SHA-1
- 代码签名 → 已禁用 SHA-1
现在的选择:
| 算法 | 输出长度 | 安全性 | 用途 |
|---|---|---|---|
| SHA-256 | 256 bit | 128 bit | 通用哈希、TLS、比特币 |
| SHA-384 | 384 bit | 192 bit | 高安全需求 |
| SHA-512 | 512 bit | 256 bit | 通用哈希(64 位 CPU 上比 SHA-256 快) |
| SHA3-256 | 256 bit | 128 bit | SHA-2 的替代方案(不同构造) |
| BLAKE3 | 可变 | 128 bit | 极快,适合高性能场景 |

HMAC 正确用法
import hmac
import hashlib
# 生成 HMAC
key = os.urandom(32) # 推荐 ≥ 32 字节
message = b"request_data"
tag = hmac.new(key, message, hashlib.sha256).hexdigest()
# 验证 HMAC — 必须用 compare_digest(恒定时间比较)
received_tag = "..." # 从请求中获取
expected_tag = hmac.new(key, message, hashlib.sha256).hexdigest()
if not hmac.compare_digest(received_tag, expected_tag):
raise ValueError("HMAC 验证失败!")
2.5 现代算法对比
ChaCha20-Poly1305 vs AES-GCM
| 对比 | AES-GCM | ChaCha20-Poly1305 |
|---|---|---|
| 硬件加速 | 需要 AES-NI 指令集 | 纯软件,无需特殊指令 |
| 无 AES-NI 时 | 慢 | 快 3-4 倍 |
| 有 AES-NI 时 | 快 | 相当 |
| Nonce 长度 | 96 位 | 96 位 |
| 安全余量 | 良好 | 更好(Poly1305 更安全) |
| TLS 1.3 支持 | ✅ | ✅ |
| 推荐场景 | 有 AES-NI 的服务器 | 移动设备、无 AES-NI 的 CPU |
💡 实战: Google 的 TLS 默认首选 ChaCha20-Poly1305(因为大量移动设备无 AES-NI)。Intel 服务器上 AES-GCM 略快。两者安全性相当,都推荐。
密码哈希函数 --- 密码存储专用
┌─────────────────────────────────────────────────────┐
│ 密码哈希算法选择指南 │
├─────────────────┬───────────┬───────────────────────┤
│ 算法 │ 推荐度 │ 说明 │
├─────────────────┼───────────┼───────────────────────┤
│ Argon2id │ 🏆 首选 │ 抗 GPU/ASIC,内存硬 │
│ bcrypt │ ✅ 推荐 │ 广泛支持,经过验证 │
│ scrypt │ ⚠️ 可用 │ 内存硬,但不如 Argon2 │
│ PBKDF2 │ ⚠️ 最低 │ NIST 推荐,但无内存硬 │
│ MD5/SHA1 │ ❌ 禁用 │ 太块,GPU 每秒亿次 │
│ SHA256 │ ❌ 禁用 │ 同上 │
└─────────────────┴───────────┴───────────────────────┘
# Python — Argon2 密码哈希(推荐)
import argon2
ph = argon2.PasswordHasher(
time_cost=3, # 迭代次数
memory_cost=65536, # 64 MB 内存
parallelism=4, # 并行线程
hash_len=32, # 输出长度
salt_len=16 # salt 长度
)
# 存储密码
hashed = ph.hash("user_password_123")
# 输出: $argon2id$v=19$m=65536,t=3,p=4$...
# 验证密码
ph.verify(hashed, "user_password_123") # 返回 True
ph.verify(hashed, "wrong_password") # 抛出 VerifyMismatchError
💡 应急响应场景 : 发现数据库存储的密码是
MD5(password)或SHA1(password)?这是严重安全事件!攻击者可以用 GPU 集群每秒尝试数亿个密码。应立即要求所有用户重置密码,并迁移到 Argon2 或 bcrypt。
3. 实际应用场景
3.1 TLS 1.3 握手流程

TLS 1.3 vs TLS 1.2 的关键改进:
| TLS 1.2 | TLS 1.3 |
|---|---|
| 2-RTT 握手 | 1-RTT 握手(快一倍) |
| 支持 RC4, 3DES, CBC | 只允许 AEAD(GCM, ChaCha20) |
| RSA 密钥传输 | 只允许 (EC)DHE(完美前向保密) |
| 静态 RSA 签名 | RSA-PSS / ECDSA |
💡 网络取证场景 : 用 Wireshark 分析 TLS 流量时,TLS 1.3 的握手比 1.2 简洁得多。如果看到
Client Hello后直接就是加密的Server Hello(没有 Certificate/Server Key Exchange),那就是 TLS 1.3。
3.2 密码存储 --- 安全实践

3.3 JWT 签名 --- HS256 vs RS256 vs ES256
JWT 结构: Header.Payload.Signature
Header: {"alg":"RS256","typ":"JWT"}
Payload: {"sub":"user123","role":"admin","exp":1723000000}
Signature: RSASHA256(Base64(Header.Payload), private_key)
| 算法 | 密钥类型 | 性能 | 适用场景 |
|---|---|---|---|
| HS256 | 对称密钥 (共享 secret) | 最快 | 单体应用,单一服务 |
| RS256 | RSA 私钥签名,公钥验证 | 慢 | 多服务,公钥可分发 |
| ES256 | ECDSA P-256 | 中等 | 移动应用,密钥小 |
⚠️ alg=none 攻击
# 攻击者可以修改 JWT 头部:
# {"alg":"none","typ":"JWT"}
# 然后删除签名部分:
# header.payload.
# 如果服务端没有验证 alg 字段,会接受无签名的 JWT!
# 正确做法:
import jwt
# ❌ 错误 — 不验证签名
payload = jwt.decode(token, options={"verify_signature": False})
# ✅ 正确 — 强制验证
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"] # 必须指定允许的算法!
)
3.4 磁盘加密 --- 密钥层次结构

3.5 区块链密码学

4. 常见攻击与漏洞
4.1 Padding Oracle Attack

现实案例: POODLE 攻击 (CVE-2014-3566) 就是 Padding Oracle 的变种,影响 SSL 3.0。攻击者通过降级攻击迫使服务端使用 SSL 3.0 的 CBC 模式,然后利用 Padding Oracle 逐字节解密 Cookie。
4.2 长度扩展攻击
问题: H(K || m) 不是安全的 MAC!
原因: Merkle-Damgård 结构 (MD5, SHA-1, SHA-2) 的输出就是内部状态。
已知 H(K || m) 和 |K||m|,可以计算 H(K || m || padding || m')
而不需要知道 K!
攻击演示:
已知: HMAC = SHA256(secret || "user=guest")
攻击者可以计算: SHA256(secret || "user=guest" || padding || "&role=admin")
而不需要知道 secret!
防御: 使用 HMAC 而不是 H(K||m)
HMAC 的两层结构 (ipad/opad) 天然防止长度扩展攻击
4.3 Nonce 重用 --- 灾难性错误
GCM 模式 Nonce 重用
# ❌ 灾难性错误:GCM Nonce 重用
nonce = os.urandom(12)
c1 = aesgcm.encrypt(nonce, message1, aad)
c2 = aesgcm.encrypt(nonce, message2, aad) # 相同的 nonce!
# 后果:
# 1. 攻击者可以计算: c1 ⊕ c2 = m1 ⊕ m2
# 2. 如果知道 m1 的部分内容,可以恢复 m2
# 3. 更严重:攻击者可以伪造认证标签 (GHASH 密钥泄露)
# 4. 整个密钥的安全性被破坏!
One-Time Pad 重用
经典案例: 苏联 Venona 计划 (1940s)
苏联情报机构使用一次性密码本加密消息
但由于生产量不足,部分密码本被重复使用
美国 NSA 通过比对两条用相同密钥加密的消息
恢复了大量苏联情报通信的明文
原理:
c1 = m1 ⊕ k
c2 = m2 ⊕ k
c1 ⊕ c2 = m1 ⊕ m2
如果 m1 和 m2 是自然语言,m1 ⊕ m2 有可识别的模式
通过统计分析可以恢复 m1 和 m2
4.4 Timing Attack --- 恒定时间比较
# ❌ 不安全 — 非恒定时间比较
def verify_signature(received_sig, expected_sig):
if len(received_sig) != len(expected_sig):
return False
for i in range(len(received_sig)):
if received_sig[i] != expected_sig[i]:
return False # 在不匹配的位置提前返回!
return True
# 攻击者可以通过测量响应时间,逐字节确定签名
# 第 1 字节匹配: +10ns
# 第 2 字节匹配: +10ns
# ...
# 暴力复杂度从 256^n 降到 256×n
# ✅ 安全 — 恒定时间比较
import hmac
def verify_signature_safe(received_sig, expected_sig):
return hmac.compare_digest(received_sig, expected_sig)
# hmac.compare_digest() 会比较所有字节,无论在哪里不匹配
# 返回时间只与输入长度有关,与内容无关
💡 代码审计场景 : 在 JWT 验证、API 密钥验证、密码比较等场景中,搜索
==比较敏感值。如果找到,替换为hmac.compare_digest()或语言等效的恒定时间比较函数。
4.5 侧信道攻击 --- 直觉理解
┌─────────────────────────────────────────────────────┐
│ 侧信道攻击类型 │
├─────────────────┬───────────────────────────────────┤
│ 攻击类型 │ 说明 │
├─────────────────┼───────────────────────────────────┤
│ Timing Attack │ 通过操作耗时推断密钥信息 │
│ │ 例: RSA 解密时间依赖私钥 d 的比特 │
│ │ 防御: 恒定时间实现 │
├─────────────────┼───────────────────────────────────┤
│ Cache Timing │ 通过 CPU 缓存命中/未命中推断访问模式│
│ │ 例: AES 查表操作的缓存侧信道 │
│ │ 防御: 使用 AES-NI 指令 (不查表) │
├─────────────────┼───────────────────────────────────┤
│ Power Analysis │ 通过功耗变化推断密钥 │
│ │ 例: 智能卡/嵌入式设备的 SPA/DPA │
│ │ 防御: 功耗均衡、随机延迟 │
├─────────────────┼───────────────────────────────────┤
│ EM Radiation │ 通过电磁辐射推断操作 │
│ │ 例: TEMPEST 攻击 │
│ │ 防御: 屏蔽室、法拉第笼 │
├─────────────────┼───────────────────────────────────┤
│ Fault Injection │ 通过电压/时钟故障诱导计算错误 │
│ │ 例: RSA-CRT 故障攻击恢复私钥 │
│ │ 防御: 错误检测、冗余计算 │
└─────────────────┴───────────────────────────────────┘
ROCA 漏洞 (CVE-2017-15361)
Infineon TPM 芯片的 RSA 密钥生成缺陷:
p 和 q 的生成使用了有偏的随机数生成器
导致 p ≡ a (mod M) 对于某个小 M
影响:
- 30,000+ 设备受影响 (智能卡、TPM、安全启动密钥)
- 攻击者可以在几小时内分解受影响的 RSA 密钥
- 不需要量子计算机!
检测:
- 工具: roca-detect (GitHub)
- 检查你的 RSA 密钥是否被影响:
$ python3 roca-detect.py your_key.pem
防御:
- 更新 TPM 固件
- 重新生成 RSA 密钥对
- 迁移到 ECC (Ed25519)
5. 学习路线与资源
5.1 学习路径


5.2 推荐资源
书籍
| 书籍 | 难度 | 特点 |
|---|---|---|
| 《Serious Cryptography》(Aumasson) | ⭐⭐ | 最佳入门,实战导向,代码示例丰富 |
| 《Understanding Cryptography》( Paar/Pezl) | ⭐⭐⭐ | 教科书级别,数学推导详细 |
| 《Applied Cryptography》(Schneier) | ⭐⭐⭐ | 经典,部分内容过时但思想永恒 |
| 《Real-World Cryptography》(Wong) | ⭐⭐ | 现代实践,TLS/Signal/加密货币密码学 |
在线课程
| 课程 | 平台 | 特点 |
|---|---|---|
| CryptoHack | cryptohack.org | 最佳实战平台,交互式,Python 代码题 |
| Cryptopals | cryptopals.com | 经典挑战,64 题,从基础到高级 |
| Cryptography I | Coursera (Stanford, Boneh) | 理论扎实,数学推导 |
| pwn.college crypto | pwn.college | CTF 风格,实践性强 |
工具
| 工具 | 用途 | 链接 |
|---|---|---|
| CyberChef | 可视化加密/编码/解码 | https://gchq.github.io/CyberChef/ |
| hashcat | 密码破解学习 | https://hashcat.net/hashcat/ |
| openssl | 命令行加密/证书操作 | 系统自带 |
| Wireshark | TLS 握手分析 | https://wireshark.org |
| cryptography (Python) | 编程实现 | pip install cryptography |
5.3 实战练习 --- 安全文件加密工具
"""
实战项目:安全的文件加密工具
功能: 使用 Argon2 从密码派生密钥,AES-256-GCM 加密文件
"""
import os
import struct
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
# 文件加密格式:
# [salt(16)] [nonce(12)] [ciphertext + tag]
def derive_key(password: str, salt: bytes) -> bytes:
"""从密码派生 256 位密钥"""
kdf = Argon2id(
salt=salt,
length=32, # 256 位密钥
iterations=3,
lanes=4,
memory_cost=65536, # 64 MB
)
return kdf.derive(password.encode())
def encrypt_file(input_path: str, output_path: str, password: str):
"""加密文件"""
salt = os.urandom(16)
key = derive_key(password, salt)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
with open(input_path, "rb") as f:
plaintext = f.read()
aad = os.path.basename(input_path).encode() # 文件名作为 AAD
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
with open(output_path, "wb") as f:
f.write(salt + nonce + ciphertext)
print(f"✅ 已加密: {input_path} → {output_path}")
print(f" 原始大小: {len(plaintext)} 字节")
print(f" 加密大小: {len(salt) + len(nonce) + len(ciphertext)} 字节")
def decrypt_file(input_path: str, output_path: str, password: str):
"""解密文件"""
with open(input_path, "rb") as f:
data = f.read()
salt = data[:16]
nonce = data[16:28]
ciphertext = data[28:]
key = derive_key(password, salt)
aesgcm = AESGCM(key)
try:
aad = os.path.basename(output_path).encode()
plaintext = aesgcm.decrypt(nonce, ciphertext, aad)
except Exception:
print("❌ 解密失败:密码错误或文件已损坏")
return
with open(output_path, "wb") as f:
f.write(plaintext)
print(f"✅ 已解密: {input_path} → {output_path}")
# 使用示例
if __name__ == "__main__":
encrypt_file("secret.docx", "secret.docx.enc", "my_secure_password")
decrypt_file("secret.docx.enc", "secret_decrypted.docx", "my_secure_password")
5.4 OpenSSL 命令行速查
# 生成 RSA-2048 私钥
openssl genrsa -out private.pem 2048
# 生成 RSA-4096 私钥
openssl genrsa -out private4096.pem 4096
# 生成 ECC 私钥 (P-256)
openssl ecparam -genkey -name prime256v1 -noout -out ec_key.pem
# 生成 Ed25519 私钥
openssl genpkey -algorithm ED25519 -out ed25519_key.pem
# 提取公钥
openssl rsa -in private.pem -pubout -out public.pem
# AES-256-GCM 加密 (OpenSSL 命令行不直接支持 GCM,用 CBC 演示)
openssl enc -aes-256-cbc -salt -in secret.txt -out secret.txt.enc
# AES-256-CBC 解密
openssl enc -aes-256-cbc -d -in secret.txt.enc -out secret.txt
# 计算 SHA-256 哈希
openssl dgst -sha256 file.txt
# 计算 HMAC-SHA256
openssl dgst -sha256 -hmac "my_secret_key" file.txt
# RSA 签名
openssl dgst -sha256 -sign private.pem -out signature.bin file.txt
# RSA 验签
openssl dgst -sha256 -verify public.pem -signature signature.bin file.txt
# 查看证书信息
openssl x509 -in cert.pem -text -noout
# TLS 连接测试
openssl s_client -connect example.com:443 -servername example.com
# 检查 TLS 版本和密码套件
openssl s_client -connect example.com:443 -tls1_3
# 查看证书的 RSA 密钥长度
openssl x509 -in cert.pem -text -noout | grep "Public-Key"
5.5 Wireshark TLS 分析
# 设置 TLS 密钥日志文件(让 Wireshark 解密 TLS 流量)
export SSLKEYLOGFILE="$HOME/tls-keys.log"
# 然后启动浏览器(Chrome/Edge 会写入密钥到该文件)
# 在 Wireshark 中: Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename
# 选择 $HOME/tls-keys.log
# 过滤 TLS 流量
tls
# 查看 TLS 握手
tls.handshake
# 查看具体的 Client Hello
tls.handshake.type == 1
# 查看 Server Hello
tls.handshake.type == 2
# 查看证书
tls.handshake.certificate
# 查看密码套件协商
tls.handshake.ciphersuite
5.6 面试常问密码学问题
-
解释对称加密和非对称加密的区别,以及 TLS 中如何结合使用?
-
为什么不能用 MD5 存储密码?应该用什么?
-
什么是 Perfect Forward Secrecy (PFS)?TLS 1.3 为什么强制要求?
-
AES-GCM 中 Nonce 重用有什么后果?
-
解释 HMAC 的构造,为什么不能直接用 H(K||m)?
-
RSA-2048 和 ECC-256 哪个更安全?为什么?
-
什么是 Padding Oracle Attack?如何防御?
-
解释 TLS 1.3 握手流程,与 1.2 有什么不同?
-
什么是 AEAD?举例说明。
-
如果你的系统发现密码用 SHA1 存储,你如何处理?
附录:密码学决策树
你需要做什么?
│
├─ 加密数据
│ ├─ 大量数据? → AES-256-GCM 或 ChaCha20-Poly1305
│ ├─ 小数据/密钥加密? → RSA-OAEP 或 ECIES
│ └─ 需要确定性加密? → AES-SIV
│
├─ 验证数据完整性
│ ├─ 不需要密钥? → SHA-256
│ ├─ 需要认证? → HMAC-SHA256
│ └─ 需要不可抵赖? → ECDSA / Ed25519
│
├─ 存储密码
│ └─ Argon2id (首选) 或 bcrypt
│
├─ 密钥交换
│ └─ X25519 (首选) 或 ECDH P-256
│
├─ 密钥派生
│ ├─ 从密码派生? → Argon2id 或 PBKDF2
│ └─ 从主密钥派生子密钥? → HKDF-SHA256
│
└─ 随机数生成
└─ os.urandom() / /dev/urandom (不要自己实现!)
本文档为 SecOps 工程师密码学参考手册,涵盖原理、应用、攻击与防御。密码学在安全运营中无处不在------从日志中的 TLS 握手分析到恶意软件加密通信解密,从密码存储合规检查到 API 签名验证。掌握这些知识,你将能在实际工作中做出更明智的安全决策。