密码学基础
密码学为数据提供机密性、完整性和来源验证等保护,但这些目标需要不同机制。加密让没有密钥的人难以读懂内容,摘要计算内容的指纹,MAC 和数字签名则把校验与密钥联系起来。把它们都叫作“加密”,会误判一个接口究竟防住了什么。
面试中的对称与非对称比较、HTTPS 握手、签名校验和密钥使用,都建立在这些区别之上。理解时应先说明保护目标,再解释算法怎样组合,最后检查密钥、随机数和重放等边界。
1. 面试核心
先记住这几条,再读正文- 对称加密由双方持有共享秘密;非对称机制使用公私钥对,分别支持加密、签名或密钥协商等不同操作,不能把所有公钥算法都理解为“用公钥加密”。卡 · 对称加密与公钥机制
- 摘要不带秘密密钥,攻击者也能为篡改后的内容重新计算摘要。校验摘要只有在预期摘要可信时,才能帮助判断内容是否被改动。卡 · 摘要不带来源证明
- MAC 使用共享密钥,签名使用私钥生成、公钥验证。能验证 MAC 的一方通常也能生成 MAC;签名验证者不需要持有签名私钥。卡 · 签名允许公开验证
- AEAD 同时加密和验证完整性,但必须正确使用密钥和 nonce。同一 AES-GCM 密钥下不能重复使用加密 nonce,验证失败不能继续处理输出。卡 · AEAD、nonce 与附加认证数据
- 混合方案用公钥机制建立或保护会话秘密,再用对称密码处理大量数据。TLS 1.3 的证书握手不等于用证书公钥加密一个会话密钥。
- 性能比较要固定算法、密钥长度、消息大小、具体操作与硬件条件。没有通用的“非对称比对称慢多少倍”或“哈希一定比加密快”结论。卡 · 保护目标与基本机制
2. 保护目标与基本机制
机密性限制谁能读懂内容;完整性用于检测数据是否被未授权地改变;来源验证用于确认数据与某个受信密钥持有者之间的关系。验证了一个密钥,并不自动知道它属于谁,这还需要证书、预先配置的信任关系等机制。
以一条转账请求为例:加密可以隐藏金额,认证标签可以帮助检测金额被修改,但收到一份昨天合法的请求时,标签依然可能有效。是否允许重复执行,需要额外的防重放和业务去重规则。
| 机制 | 输入中的秘密 | 主要结果 | 单独不能解决什么 |
|---|---|---|---|
| Base64 编码 | 无 | 把字节表示成便于传输的字符 | 保密、来源验证 |
| SHA-256 摘要 | 无 | 固定长度的内容摘要 | 对抗攻击者同时替换内容和摘要 |
| HMAC-SHA-256 | 共享密钥 | 消息认证码 | 保密、区分共享同一密钥的不同持有者 |
| 数字签名 | 签名私钥 | 可用对应公钥验证的签名 | 保密、未经绑定的公钥身份、自动防重放 |
| AEAD 加密 | 共享密钥 | 密文与认证标签 | 应用权限、业务去重、隐藏所有元数据 |
这些机制不是互相替代的等级关系。例如,把密文做 Base64 编码便于传输没有问题,但把明文做 Base64 编码后就认为已经保密,是混淆了表示形式与安全机制。
3. 对称加密与公钥机制
对称加密使用共享的秘密密钥完成加密和解密。AES 是对称分组密码;实际保护消息时,还需要按协议选择工作方式、处理完整性等要求,不能只说“用了 AES”就认为方案完整。
非对称密码使用相互关联的公钥和私钥。公钥可以分发给别人,私钥由持有者保管,但两把钥匙能做什么,取决于具体算法和协议:
- 公钥加密中,发送者用接收者的公钥加密,接收者用自己的私钥解密,例如 RSA-OAEP。
- 数字签名中,签名者用私钥生成签名,其他人用对应公钥验证,例如 Ed25519。
- 密钥协商中,双方交换公开参数,再结合各自的私有信息计算共享秘密,例如临时 ECDH。它不是把自己的私钥发给对方。
RSA 的基础运算涉及大整数模幂;椭圆曲线机制使用曲线上的运算。面试里可以说明这些数学工具承担的角色,但应用代码应调用经过维护的密码库,不自行拼接裸 RSA 运算、填充或签名格式。
公钥不需要保密,不代表可以不验证来源。如果攻击者把“服务器的公钥”替换成自己的公钥,客户端可能与攻击者建立加密通信。证书链和目标名称校验用于建立身份与公钥之间的信任关系,具体见 TLS。
4. 摘要、MAC 与数字签名
4.1 摘要不带来源证明
密码学哈希把输入映射为固定长度的摘要,例如 SHA-256 输出 256 位,也就是 32 字节。它不提供一个持有密钥就能还原原文的解密操作。摘要空间有限,碰撞必然存在;密码学要求的是在给定安全条件下,找到所需碰撞或原像在计算上困难,而不是“绝对不会重复”。
假设文件旁边放着一个 SHA-256 值。若文件和摘要都来自可被攻击者修改的同一个渠道,攻击者可以替换文件,再重新计算摘要。只有预期摘要通过可信渠道取得,或者由受信签名保护,比较才有相应的安全意义。
摘要不可直接解密,也不代表低熵内容无法猜测。若输入只有几个可能值,攻击者可以逐个计算摘要并比较。不能把手机号等可枚举数据简单哈希后,就自动视为无法识别。
4.2 MAC 把校验绑定到共享密钥
消息认证码(Message Authentication Code,MAC)根据秘密密钥和消息计算标签。发送者生成标签,接收者用同一密钥重新计算并比较;不知道密钥的人不能只靠公开算法,为任意篡改消息生成有效标签。
HMAC 是基于哈希函数构造 MAC 的标准方式,例如 HMAC-SHA-256。不要自行用“密钥与消息拼接后做一次哈希”代替它。Go 标准库用 hmac.New(sha256.New, key) 计算,验证时用 hmac.Equal 比较标签,避免普通比较带来的时序侧信道问题。
HMAC 不加密消息,旁观者仍能读到明文;它也不区分所有共享同一密钥的服务,因为这些服务都具备生成标签的能力。MAC 能证明消息与这组密钥持有者有关,不能仅凭标签断定具体是哪一个成员生成的。
4.3 签名允许公开验证
数字签名由签名者使用私钥生成,验证者使用对应公钥检查消息与签名是否匹配。以 Ed25519 为例,签名和验签是专门的算法,没有“拿公钥解密签名,就得到原文”的流程。
签名不是“私钥加密”。这种说法混淆了目标,也不适用于所有算法。签名保护消息的可验证性,消息本身可以完全公开;只有加密机制才负责隐藏内容。
签名通过之后,还要知道公钥是否可信、签名究竟覆盖了哪些字节,以及消息是否适用于当前场景。签名一个请求体,但不覆盖关键的路径或用途标识,可能让同一内容被放到错误的上下文里。算法不能代替协议对消息范围和身份的约束。
5. AEAD、nonce 与附加认证数据
AEAD(Authenticated Encryption with Associated Data)是同时保护机密性与完整性的加密方式。它接收密钥、nonce、明文和附加认证数据,输出密文与认证标签;解密时也检查标签,检查失败就拒绝。
nonce 是一次加密操作使用的值。对于 AES-GCM,同一密钥下不同加密操作的 nonce 必须不同;它通常不需要保密,可以随密文一起发送。重复使用可能同时破坏机密性与完整性,并非只是让密文看起来有规律。
附加认证数据(AAD)参与完整性校验,但不被加密。例如,消息头里的租户标识 tenant:7 可以保持可读,同时与密文绑定。接收者要把自己期望的上下文传给解密函数,不能无条件相信攻击者随请求提供的任意标识。
下面用 Go 1.22 标准库演示一次 AES-GCM 加密、解密与篡改检测。示例每次运行生成一把新密钥,仅加密一条消息:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
)
func main() {
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
panic(err)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
nonce := make([]byte, aead.NonceSize())
if _, err := rand.Read(nonce); err != nil {
panic(err)
}
aad := []byte("tenant:7")
ciphertext := aead.Seal(nil, nonce, []byte("amount=100"), aad)
plaintext, err := aead.Open(nil, nonce, ciphertext, aad)
if err != nil {
panic(err)
}
fmt.Println(string(plaintext))
damaged := append([]byte(nil), ciphertext...)
damaged[len(damaged)-1] ^= 1
_, err = aead.Open(nil, nonce, damaged, aad)
fmt.Println("tamper rejected:", err != nil)
_, err = aead.Open(nil, nonce, ciphertext, []byte("tenant:8"))
fmt.Println("wrong context rejected:", err != nil)
}
输出依次是 amount=100、tamper rejected: true 和 wrong context rejected: true。Seal 的输出包含认证标签;修改密文或换用不同的 AAD,都使 Open 拒绝。代码不会在验证失败后继续使用返回值。
本例的 nonce 长度是 12 字节,认证标签带来 16 字节开销。nonce 若也随消息传输,还需另外计算它的开销。示例没有实现密钥保存、消息封装或轮换,不能直接当成完整通信协议。
5.1 随机生成 nonce 就没有重复风险了吗?
没有这种保证。随机数空间有限,使用次数增加时会积累碰撞风险。Go 1.22 的 GCM 示例明确限制:同一密钥下不要使用超过 2^32 个随机 nonce。这是为了限制重复风险,不表示在边界之前重复概率为零,也不表示其他协议的所有使用限额都相同。
如果用计数器分配 nonce,需要保证同一密钥的所有发送实例不会分配重复值,进程重启也不能把计数器清零后继续使用旧密钥。密钥本身应由密码学安全随机源生成,例如 crypto/rand,不能用时间戳或普通伪随机序列替代。
5.2 AEAD 验证通过,是否说明请求没有被重放?
不是。攻击者原样复制一份合法的 nonce、密文和 AAD,解密验证仍可能通过,因为这些字节确实曾由合法密钥生成。禁止加密端重复使用 nonce,与接收端识别已经处理过的请求,是两件事。
协议需要用请求序号、有效期与已用标识等约束处理重放;带副作用的业务还要设计去重和恢复,见分布式流程恢复。
6. 混合方案与性能比较
混合加密方案把公钥机制与对称加密组合起来:先建立或保护短的会话秘密,再由对称密钥处理大量内容。对称算法适合持续处理数据;公钥机制承担身份验证、密钥协商或短秘密传输等职责,不必对每一段业务内容都执行昂贵的公钥运算。
公钥加密还有消息长度约束。例如,RSA-OAEP 的输入长度取决于模数大小和哈希输出长度:使用 2048 位 RSA 与 SHA-256 时,单次最多加密 256 - 2 × 32 - 2 = 190 字节。这个例子用于说明算法接口边界,不能把大文件切成许多段后就当成完整安全协议。
TLS 1.3 的完整证书握手使用临时密钥交换派生共享秘密,用证书对应的私钥签名证明身份,最后由对称 AEAD 保护应用数据。它没有采用“客户端生成会话密钥,再用服务器证书公钥加密发送”的静态 RSA 密钥传输流程。详细握手见 TLS。
比较开销时,至少需要说明以下条件:
| 条件 | 为什么影响结果 |
|---|---|
| 具体操作 | RSA 加密与解密、签名与验签的成本可能不同;密钥生成又是另一种操作 |
| 算法与参数 | AES-GCM、SHA-256、HMAC-SHA-256 和公钥算法的职责、处理步骤不同 |
| 数据大小 | 短消息容易受调用和初始化开销影响,大消息更关注吞吐 |
| 硬件与实现 | AES、哈希指令加速以及库实现都会影响结果 |
| 系统范围 | 握手的网络往返、证书验证、随机数生成不能与纯算法耗时混为一谈 |
所以,不能回答一个脱离条件的“慢多少指数级”,也不能为了降低开销,用 SHA-256 替代需要保密的加密操作。先满足安全职责,再在相同环境和目标负载下测量对应操作;本文不提供跨算法通用的性能倍数。
密钥泄露后,算法再正确也无法维持原有信任。密钥应按用途分离,并控制存储、访问、轮换和撤销,相关流程见密钥、证书、审计与供应链安全。
7 · 面试题卡片
本页题目与参考回答
7.1 对称与非对称密码怎样分工,为什么业务数据通常用对称加密?
考察重点:共享密钥与公私钥、加密和密钥协商的区别,以及混合方案的成本与边界。
参考回答
**对称加密适合处理大量业务数据,公钥机制用于身份验证、密钥协商或保护短的会话秘密,两者可以组合使用。**对称方式要求双方持有共享密钥,公钥机制则通过公私钥分配不同能力。
公钥运算成本和输入约束都与具体算法有关,不能给出通用速度倍数。以 TLS 1.3 证书握手为例,临时密钥交换派生密钥,证书私钥用于签名证明身份,随后用对称 AEAD 保护数据;它不是用证书公钥直接加密所有业务内容。
7.2 加密和哈希的开销怎样比较?
考察重点:安全目标、算法参数、消息大小与测量范围。
参考回答
**不能按名称给出固定快慢,也不能用哈希替代保密所需的加密。**哈希生成摘要,不提供持钥解密;加密需要让合法接收者恢复内容,职责不同。
比较时固定算法、参数、消息长度、硬件和库实现,分别测量初始化、处理耗时与吞吐。若讨论 HTTPS 总开销,还要考虑握手、证书验证和网络往返,不能只比较一个哈希调用和一次完整连接。
7.3 消息附带 SHA-256 摘要,为什么仍可能被攻击者篡改?
考察重点:无密钥摘要与带密钥认证的区别,预期摘要的可信来源。
相关内容:摘要不带来源证明、MAC 把校验绑定到共享密钥。
参考回答
**攻击者若能同时替换消息和摘要,就能为篡改后的消息重新计算 SHA-256。**摘要本身不含秘密,不能证明是谁生成的。
可以通过可信渠道取得预期摘要,或使用受信签名、基于共享秘密的 MAC 保护消息。HMAC 的接收者用共享密钥验证,标签比较应使用相应的安全接口;它仍不负责隐藏消息内容。
7.4 数字签名为什么不能简单称为“私钥加密”?
考察重点:签名与保密的职责、公钥信任,以及公开验证与共享密钥验证的差别。
相关内容:签名允许公开验证、MAC 把校验绑定到共享密钥。
参考回答
**签名用于生成可公开验证的证明,不负责隐藏消息;签名和加密也不是所有算法中互相反转的操作。**例如 Ed25519 用私钥签名、公钥验签,没有通过“公钥解密”恢复原文的步骤。
验证者还要信任公钥与身份的绑定,并检查签名覆盖的消息和使用场景。与 HMAC 相比,持有公钥的验证者不需要获得生成签名的私钥。
7.5 AES-GCM 为什么不能重复使用加密 nonce,认证通过又为什么不等于防重放?
考察重点:同一密钥下的 nonce 唯一性、认证标签与应用请求历史。
相关内容:AEAD、nonce 与附加认证数据、认证与重放的区别。
参考回答
**同一 AES-GCM 密钥下重复使用加密 nonce 会破坏安全前提,而重放是再次提交已经合法生成的完整消息。**前者需要加密端管理 nonce,跨实例和重启都不能发生重复;随机生成也需要限制同一密钥的使用次数。
后者可能仍通过认证,因为密文没有被修改。接收端需要结合请求序号、已用标识和业务去重判断是否再次执行;验证失败时则必须拒绝,不能继续处理输出。
8. 参考资料
- RFC 5116:AEAD 接口、nonce 与 AES-GCM,第 2、3、5.1 节
- RFC 2104:HMAC 机制
- RFC 8032:Ed25519 签名与验证,第 5.1.6、5.1.7 节
- RFC 8017:RSA-OAEP 输入边界,第 7.1.1 节
- RFC 8446:TLS 1.3 握手与记录保护
- Go 1.22.12:AEAD 接口
- Go 1.22.12:GCM 示例及随机 nonce 使用限制
- Go 1.22.12:HMAC 与标签比较
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明