这是结诚浩的《图解密码技术》一书的读书笔记,我是在去北京的火车上把其中感兴趣的部分草草过完的。World of Z2O-K7E 只是建议过一下,我就只是过一下,感觉还是很有帮助的,至少看过了之后,我能感觉自己的大脑切换到了“密码学的上下文”,接收零知识证明相关的知识似乎更高效了。
基本常识
- 不要使用保密的密码算法(即隐蔽式的安全性),应该使用得到广为验证的公开的密码算法,而保护好密钥。
- 使用低强度的密码会给人一种错误的安全感,比不进行加密更危险。
- 任何密码总有一天会被破解,除了不实用的一次性密码本和传说中的量子密码。
- 密码只是信息安全的一部分,提防“社会工程学”。
对称密码
DES / 3DES
一种将 64 位明文加密为 64 位密文的对称密码算法,需要以分组为单位对输入进行处理(这样的密码算法称为分组密码,block cipher)。DES 已经可以在现实时间内被破解。
三重DES(3DES)是为了增加 DES 的强度设计的。三重 DES 并不是三轮加密,而是加密、解密、加密。如果三轮的密钥都相同,就跟 DES 一样了。
第一轮和第三轮使用同样的密钥则称为 DES-EDE2。
三轮都不同则称为 DES-EDE3。
AES
Rijndael 算法在 2000 年被选为 AES。
分组长度固定为 128 比特,密钥长度有 128、192、256 比特三种。
加密过程:SubBytes -> ShiftRows -> MixColumns -> AddRoundKey
解密过程: AddRoundKey -> InvMixColumns -> InvShiftRows -> InvSubBytes
如何选择
不用 DES,少用 3DES,尽量用 AES。
分组密码的模式
- ECB(Electronic CodeBook,电子密码本):简单,快速,支持并行计算;明文中重复排列会反映在密文中,并且通过删除、替换密文分组可以对明文进行操作,对包含某些比特错误的密文解密时对应的分组会出错,不能抵御重放攻击。不要用。
- CBC(Cipher Book Chaining,密文分组连接模式):推荐使用,重复的明文不会导致重复的密文,可以解密任意密文分组。必须是先加密再 XOR,如果反过来,则其效果等同于 ECB,可以直接从 IV (初始化向量)开始,依次将前后两组密文执行 XOR,得到的每组结果恰好就等于 ECB 每组的结果。
- CFB / OFB:不需要填充(padding)。
- CTR(CounTeR,计数器模式):推荐使用,主动攻击者反转密文分组中的某些比特时,明文分组中相对应的比特也会被反转。
公钥密码
RSA 的基本步骤:
- 取两个比较大的质数 p 和 q。
- 求两者的乘积 $N = p \times q$。
- 求 ,即 p 和 q 的最小公倍数;有的资料说的是求欧拉函数值 $\phi = (p-1) \times (q-1)$,以下统称 $\phi$。
GPT: 两者都有各自的使用场景,但在实际使用中,求欧拉函数值 ($\phi$) 更为常见。
- 选择公钥 $E$,$E$ 与 $\phi$ 互质并且 $1 < E < \phi$。
- 求私钥 $D$,$D \times E \equiv 1$ (mod $\phi$),即 D 为 E 的乘法逆元。
- 这样得到公钥为 $(E, N)$,私钥为 $(D, N)$。
一个使用小质数实现的简单的 RSA:
1 | use num::BigInt; |
单向散列函数
单向散列函数(也称为消息摘要函数、哈希函数、杂凑函数),用来保证消息的完整性(integrity),也称为一致性。
输入的消息也称为原像(pre-image)。
输出的散列值也称为消息摘要(message digest)或者指纹(fingerprint)。
难以发现碰撞的性质称为抗碰撞性(collision resistance)。
算法选择
MD5 是不安全的。
SHA-1 不应该用于新用途。
SHA-2 (SHA-256以上) 、SHA-3 是安全的,可以使用。
消息认证码
消息认证码(Message Authentication Code)是一种确认完整性并进行认证的技术。
实现方法
- 单向散列函数,例如 HMAC。
- 分组密码,例如 AES-CMAC。
- 其他,例如流密码和公钥密码。
数字签名
使用公钥密码(比如 RSA、椭圆曲线)来实现。
- 绝对不要对意思不清楚的消息进行签名,可能无意中解密了公钥加密的密文。
- 依赖公钥基础设施,公钥必须属于真正的发送者,即使用证书来确认。
密钥交换
Diffie-Hellman
一个使用小质数实现的简单的 Diffie-Hellman:
1 | use num::BigInt; |
椭圆曲线密码
椭圆曲线密码是利用“椭圆曲线上的离散对数问题”的复杂度来实现的。
使用了椭圆曲线的 schnorr 的一个简单实现:
1 | use k256::{NonZeroScalar, ProjectivePoint, PublicKey}; |