从零讲清 DES、3DES、RC4 与 Blowfish
一句话:这四种密码是 1990 年代的主力 —— DES 及其三重加密的权宜之计 3DES、流密码 RC4,以及 Blowfish —— 如今它们全部退役或被废弃,只因旧文件、旧硬件和旧协议仍需读取才得以存活。
DES:1977 年的标准,一切的起点
它是什么。数据加密标准(FIPS 46,1977)是对 64 位分组进行 16 轮 Feistel 运算的密码,密钥 56 位(因每字节有一位用于奇偶校验,故存为 8 字节)。它由 IBM 设计、NSA 参与,作为美国标准使用了二十年;1998 年 EFF 的 Deep Crack 机器用 25 万美元的硬件在 56 小时内暴力破解了一个密钥,它由此在政策层面被判死刑。NIST 于 2005 年撤销了它的加密用途。
为什么奇偶位会让密钥长度的说法混乱。DES 密钥的每第 8 位都由其余七位算出,因此 8 字节密钥只含 56 个独立位 —— 这就是同一个算法既被称作“64 位密钥”又被称作“56 位密钥”的原因。3DES 继承了这一点:24 个原始字节 = 168 原始位 = 112 有效位(余下的被中间相遇攻击抵消)。
如今在哪里还会遇到它。银行 PIN 块(ISO 9564)、部分 Kerberos v4 域、旧备份归档和智能卡。如果一个系统今天仍要求用 DES,那么它要么是在与 1990 年代的硬件互通,要么就是不安全的。
3DES:加密、解密、再加密
它的手法。三重 DES 对每个分组跑三遍 DES:EDE3 用三个不同密钥(C = Ek3(Dk2(Ek1(P))));中间那一步是解密,这样只要令 k1=k2,3DES 实现就能模拟单重 DES。EDE2 只用两个密钥(k1=k3),有效强度 112 位。这个构造是个权宜之计:不必发明新算法,就把一个已被攻击的算法的安全性提高了三倍。
它终究还是消亡的原因。两个问题:它慢(每个分组三遍完整 DES,约为 AES 速度的 1/20),而且沿用了 64 位分组。当同一密钥下处理 32 GiB 数据(即 232 个分组的生日边界)时,分组碰撞频繁到足以泄露明文结构 —— 2016 年的 Sweet32 攻击就在 TLS 的 3DES 会话上演示了这一点。NIST 自 2023 年起不允许再用 3DES 加密(SP 800-131A r2);PCI DSS 要求支付系统停用 3DES。
RC4 / ARCFOUR:早期 Web 的流密码
它如何工作。RC4(Ron's Code 4,1987 年设计,1994 年泄露)是流密码:密钥先调度出一个 256 字节的置换(KSA),随后该置换输出伪随机字节流(PRGA)与消息异或。没有填充、没有分组长度限制、任意消息长度、软件实现极快 —— 这正是它在 1990 年代统治 SSL/TLS 和 WEP 的原因。“ARCFOUR”是多数开源项目为避免商标问题而使用的名字。
它是怎么被攻破的。Fluhrer-Mantin-Shamir(2001)指出密钥流的前若干字节存在偏差 —— 这对 WEP 是致命的,因为 WEP 的每包密钥构造恰好把这些字节送到攻击者面前。后来的偏差攻击(2013 年,AlFardan 等人)通过数百万次连接从 RC4 保护的 TLS 会话中恢复了明文。RFC 7465(2015)彻底禁止在 TLS 中使用 RC4。
经典测试向量。密钥“Key”(3 字节)、消息“Plaintext”得到密文 bbf316e8d940af0ad3 —— 可点 RC4 示例对比。本页自检还校验 RFC 6229 中 40 位密钥 01 02 03 04 05 的密钥流:b2396305f03dc027ccc3524a0a1118a8。
Blowfish:未被攻破,但受分组长度限制
它是什么。Bruce Schneier 1993 年的 Feistel 密码:64 位分组,密钥 32 至 448 位(4..56 字节),16 轮,以及一个著名的密钥调度 —— 它把 Blowfish 本身运行 521 次来生成子密钥,这使得密钥初始化很慢,但暴力破解的代价同样很高。目前没有实用的密码分析能攻破完整轮数的 Blowfish。
那它为什么还在这页上。问题在 64 位分组,而不在算法本身:同一密钥下超过约 32 GiB 后,生日碰撞就会泄露数据(与 3DES 相同的边界,Sweet32 式攻击)。Schneier 本人建议任何新用途都改用他的后继者 Twofish(AES 决赛算法)或 AES。Blowfish 仍活在 bcrypt 内部 —— 那个口令哈希的密钥调度就是 Blowfish —— 这没问题,因为哈希处理的输入极小,不涉及 GB 级数据。
经典测试向量。全零密钥加密全零明文得到 4ef997456198dd78(示例输出的首个分组);密钥“abcdefghijklmnopqrstuvwxyz”加密“BLOWFISH”得到 324ed0fef413a203。两者都出自 Schneier 公开的向量文件,并由本页自检校验。
常见错误
- 用它们加密新数据。除 Blowfish(受分组长度限制)外,本页每种算法都已被废弃。正确的现代选择是 AES-GCM —— 参见 AES 加密/解密。
- 用口令当 DES 密钥。8 个字符的口令并不是 56 个随机位,每个字符只有几个比特的熵。老系统正是这么做的,这也是“DES 加密”的归档文件几分钟内就被字典攻破的最常见原因。
- 在 CBC 模式复用 IV。同一密钥下,首块会泄露不同消息之间明文是否相等。“随机 IV”按钮不是摆设。
- 复用 RC4 密钥流。RC4 对同一密钥产生相同的密钥流:用一把密钥加密两条消息等于两次一密 —— 把两段密文异或,两段明文就都出来了。WEP 的致命缺陷正是这种密钥流复用构造。
- 以为“能解密就是对的”。这里的 ECB/CBC 在解密时只校验填充 —— 密钥错误时通常解出一堆乱码,但仍可能碰巧通过填充检查。任何传统模式都没有完整性校验;如果你需要,应该选 AES-GCM,而不是这些算法。
- 把 3DES 当成“168 位”。原始密钥是 168 位,但有效强度只有 112 位 —— 中间相遇攻击把三重加密降到略高于两重的水平,正如它对 3DES 的前身构造所做的那样。
常见问题
既然算法已破,为什么还保留这个工具?为了兼容。加密过的备份、导出的旧格式文件、抓取的协议报文、CTF 题目,以及无法升级的硬件,都还在说这些算法。读取旧数据是正当的,用它们写新数据则不是。
我的 DES 密钥奇偶位不对 / 含非 ASCII 字节。DES 会忽略每个密钥字节的奇偶位,因此有效密钥始终是其余 56 位。本页接受任意 8 字节,不强制偶校验。
为什么解密出来的文本有时像乱码?密钥错、模式错、IV 错,或者数据的加密方式与你设想的不同。没有认证标签就没有干净的“密钥错误”提示 —— 填充校验是唯一的线索,而且它有可能碰巧通过(约 1/256 的概率)。
Blowfish 对我的文件安全吗?小文件在密码学意义上是安全的(没有实用攻击)。但同一密钥下处理 GB 级数据就不行 —— 64 位分组的生日边界取决于数据量,而非密钥强度。用 Twofish 或 AES 可以避开这个问题。
我在别处看到的 72 字节 Blowfish 密钥是怎么回事?有些实现最多接受 72 字节,但 Schneier 的参考实现用 56 字节,而且在标准密钥调度下,超出部分的字节不会带来额外的扩散。本页遵循参考实现:4..56 字节。