1. 安全目标与威胁模型
1.1 四大安全目标
| 目标 | 含义 | 主要手段 |
|---|---|---|
| 机密性 Confidentiality | 内容不被未授权者读取 | 对称/非对称加密 |
| 完整性 Integrity | 内容未被篡改 | 哈希、MAC |
| 认证 Authentication | 确认对方身份 | 数字签名、证书 |
| 不可否认 Non-repudiation | 发送方无法抵赖 | 数字签名 |
1.2 威胁模型
密码学安全性必须相对攻击者能力来定义,常见模型由弱到强:
- 唯密文攻击:只有密文。
- 已知明文攻击:掌握若干明文密文对。
- 选择明文攻击(CPA):可任意加密,观察密文。
- 选择密文攻击(CCA):还可解密任意密文,现代协议要求能抗 CCA。
核心原则(Kerckhoffs 原则):算法公开、安全性只依赖密钥保密。任何「靠算法保密」的方案都不可靠。
2. 对称加密
2.1 基本模型
对称加密用同一把密钥加解密,速度极快(GB/s 级),适合加密数据本身。
密文 = E(K, 明文)
明文 = D(K, 密文)
2.2 AES
AES 是当今标准的分组密码,分组长度固定 128 位,密钥长度 128/192/256 位。其内部轮函数包含四个步骤:
- SubBytes:字节代换(S 盒,非线性)。
- ShiftRows:行移位(扩散)。
- MixColumns:列混合(扩散)。
- AddRoundKey:与轮密钥异或。
128 位密钥执行 10 轮,256 位执行 14 轮。设计精髓:混淆(非线性)+ 扩散(雪崩),使密文与明文、密钥的关系高度复杂。
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
key = AESGCM.generate_key(bit_length=256)
aes = AESGCM(key)
nonce = os.urandom(12) # 每次加密必须唯一
ct = aes.encrypt(nonce, b"secret", b"aad") # 返回 密文+认证标签
pt = aes.decrypt(nonce, ct, b"aad")
2.3 密钥分发难题
对称加密的死穴:n 个用户两两通信需要 n(n-1)/2 把密钥,且如何安全地把密钥送到对方手里?这正是非对称加密要解决的问题。
3. 分组密码工作模式
3.1 常见模式
| 模式 | 特点 | 是否推荐 |
|---|---|---|
| ECB | 每块独立加密,相同明文出相同密文 | 禁止使用 |
| CBC | 链接前一块密文,需随机 IV | 可用但易被 padding oracle 攻击 |
| CTR | 把分组密码当流密码,可并行 | 可用,nonce 绝不可重用 |
| GCM | CTR + GMAC,加密同时认证 | 首选(AEAD) |
| CCM | CTR + CBC-MAC | 资源受限场景 |
3.2 ECB 的经典缺陷
ECB 下相同明文块产生相同密文块,加密位图时轮廓清晰可见:
明文块: [A][A][B][B]
ECB密文: [X][X][Y][Y] ← 泄露了重复模式
3.3 选择建议
新项目一律用 AEAD 模式(AES-GCM 或 ChaCha20-Poly1305),一次调用同时提供机密性与完整性,避免「加密了但没认证」这类经典漏洞。密钥随机、nonce 唯一、认证标签必须校验。
4. 非对称加密
4.1 数学基础
非对称加密使用成对的公私钥,安全性依赖数学难题:
- RSA:大整数分解难题。
n = p × q,公钥(n, e),私钥(n, d),满足e·d ≡ 1 (mod φ(n))。 - ECC:椭圆曲线离散对数难题。256 位 ECC 安全性约等于 3072 位 RSA,密钥更短、更快。
- DH/ECDH:密钥交换,双方在不安全信道上协商出共享密钥。
4.2 加密与签名是两个方向
加密: 公钥加密 → 私钥解密 (谁都能加密,只有我解密)
签名: 私钥签名 → 公钥验签 (只有我能签,谁都能验证)
4.3 代码示例
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
# 加密(只能加密很短的数据,实际用混合加密)
ct = pub.encrypt(b"hi", padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None))
# 签名
sig = priv.sign(b"message", padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256())
pub.verify(sig, b"message", padding.PSS(...), hashes.SHA256())
4.4 混合加密
非对称加密慢且长度受限,实际协议(TLS)采用混合加密:
① 用非对称密钥交换协商出一把临时对称密钥
② 后续数据全部用对称加密(AES-GCM)传输
既解决了密钥分发,又保留了对称加密的性能。
5. 哈希函数
5.1 三大性质
- 抗原像性:给定
h,无法反推出m。 - 抗第二原像性:给定
m1,找不到m2 ≠ m1使h(m1) = h(m2)。 - 抗碰撞性:找不到任意一对
m1 ≠ m2使哈希相同(生日攻击约束下需要 2^(n/2) 复杂度)。
5.2 雪崩效应
输入改变 1 个比特,输出约一半比特翻转:
SHA256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e...
SHA256("hellp") = b7e1b3c3d4e0b3d8b1e3...(完全不同)
5.3 算法选型
| 算法 | 输出长度 | 状态 |
|---|---|---|
| MD5 | 128 位 | 已破解,禁止用于安全 |
| SHA-1 | 160 位 | 已碰撞(SHAttered),禁止 |
| SHA-256/512 | 256/512 位 | 安全,广泛使用 |
| SHA-3 | 可变 | 海绵结构,与 SHA-2 互补 |
| BLAKE3 | 可变 | 极快,非密码学场景友好 |
注意:哈希 ≠ 加密。哈希不可逆,不能「解密哈希」,只能暴力或彩虹表碰撞。
5.4 密码存储
# 错误:直接存哈希 → 彩虹表秒破
# 正确:加盐 + 慢哈希(bcrypt / scrypt / Argon2)
import bcrypt
hashed = bcrypt.hashpw(b"password", bcrypt.gensalt(rounds=12))
assert bcrypt.checkpw(b"password", hashed)
加盐防止彩虹表与相同密码同哈希;慢哈希(可调代价参数)抬高暴力破解成本。
6. 消息认证码与密钥派生
6.1 HMAC
HMAC 用密钥 + 哈希构造消息认证码,验证完整性与来源,但不提供不可否认性(双方共享密钥,谁都能算):
HMAC(K, m) = H((K ⊕ opad) || H((K ⊕ ipad) || m))
工程要点:比较 MAC 必须用常数时间比较,普通 == 会因短路提前返回而泄露信息(时序攻击)。
import hmac
hmac.compare_digest(a, b) # 常数时间比较
6.2 密钥派生 KDF
从密码派生密钥不能直接用哈希,要用专门的慢 KDF:
- PBKDF2:迭代哈希,标准但抗 GPU 能力弱。
- scrypt:内存硬,抗 ASIC。
- Argon2:现代首选,可调内存/时间/并行度,密码哈希竞赛冠军。
- HKDF:从已有高熵密钥材料扩展出多把子密钥(TLS 用)。
7. 数字签名与证书
7.1 签名流程
签名: sig = Sign(私钥, H(消息))
验证: Verify(公钥, H(消息), sig) == true
先哈希再签名是标准做法:既压缩了数据量,又固定了签名长度。
7.2 证书链
数字签名解决了「公钥是谁的」问题:证书 = 权威机构(CA)用自己私钥对「主体公钥 + 身份信息」的签名。
根 CA(自签,预置在系统/浏览器)
└── 中间 CA(由根 CA 签发)
└── 服务器证书(由中间 CA 签发,含域名与公钥)
验证时沿链逐级验签直到信任锚。中间 CA 的存在让根私钥可以离线保存,降低泄露风险。
openssl s_client -connect example.com:443 -showcerts # 查看证书链
openssl x509 -in cert.pem -text -noout # 解析证书
8. TLS 握手与工程实践
8.1 TLS 1.3 握手流程
Client Server
│── ClientHello (支持的套件/随机数) ──▶│
│◀── ServerHello (选定套件/随机数) ────│
│◀── 证书 + CertificateVerify ────────│ (服务器认证)
│── Finished ────────────────────────▶│
│◀──────── 应用数据(对称加密)────────│
TLS 1.3 把握手压缩到 1-RTT(会话恢复可 0-RTT),并强制前向保密(临时 ECDHE 密钥),废弃了 RSA 密钥传输、CBC、RC4 等不安全选项。
8.2 前向保密
使用**临时密钥交换(DHE/ECDHE)**后,即使服务器长期私钥泄露,也无法解密历史会话——因为每次会话的对称密钥是临时协商的、用完即弃。
8.3 工程清单
- 启用 TLS 1.2/1.3,禁用 SSLv3、TLS 1.0/1.1。
- 优先 ECDHE + AES-GCM 或 ChaCha20-Poly1305。
- 启用 HSTS、OCSP Stapling,配置证书自动续期(如 ACME)。
- 密钥材料放 KMS/HSM,不落配置文件、不进 Git。
9. 常见陷阱
- 自己发明加密算法:几乎必然出错,用成熟库与标准算法。
- ECB 模式:泄露明文模式,明文一图流。
- nonce/IV 重用:CTR、GCM 下重用 nonce 会直接泄露明文异或结果,是灾难性错误。
- 只加密不认证:缺少 MAC 会遭篡改与 padding oracle 攻击,应使用 AEAD。
- 用 MD5/SHA-1 做安全用途:均已碰撞,仅可用于非安全校验。
- 明文存密码或快速哈希:必须加盐 + 慢 KDF(Argon2/bcrypt)。
- 时序侧信道:密钥、MAC、Token 比较必须常数时间。
- 随机数用
random:安全场景必须用密码学安全随机源(secrets、/dev/urandom)。
参考文章
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。