第4章 对称加密实战(DES/3DES)

对称加密,说白了就是加密和解密用同一把钥匙。你想想看,就像你家的门锁,用钥匙锁上,再用同一把钥匙打开。DES 和 3DES 就是这类算法里的老前辈了。虽然现在 AES 是主流,但很多遗留系统、金融终端、智能卡里,DES/3DES 依然在跑。我去年还帮一个银行客户排查过 3DES 的兼容性问题,嗯,这玩意儿真没那么容易退休。

DES 算法原理:一个 56 位的“铁锁”

DES 的全称是 Data Encryption Standard,数据加密标准。它用 56 位的密钥,对 64 位的数据块进行加密。为什么是 56 位?因为早期设计时,密钥里每 8 位有一位是奇偶校验位,实际有效位就是 56 位。

它的核心流程,我习惯把它拆成三步:

  1. 初始置换(IP):把 64 位明文按一张固定表重新排列顺序。说白了就是打乱一下。
  2. 16 轮迭代:这是最核心的部分。每一轮都用子密钥对数据进行混淆和扩散。具体包括扩展置换、与子密钥异或、S 盒替换、P 盒置换。
  3. 逆初始置换(IP⁻¹):把 16 轮后的结果再打乱一次,得到密文。

这里有个关键点:S 盒。DES 有 8 个 S 盒,每个 S 盒是一个 4×16 的查找表。输入 6 位,输出 4 位。这是 DES 唯一的非线性部分,也是整个算法的安全基石。我在项目中遇到过有人自己改 S 盒想“增强”安全性,结果反而把算法搞废了——千万别动 S 盒,那是经过严格数学证明的。

核心要点:DES 的强度完全依赖于 56 位密钥和 S 盒的非线性。56 位密钥空间是 2⁵⁶,约 7.2×10¹⁶ 种可能。在 1998 年,电子前沿基金会用 25 万美元的专用机器,56 小时内就暴力破解了一个 DES 密钥。所以,现在单 DES 已经不安全了。

3DES 的兼容性:用三次加密弥补短板

3DES 不是新算法,而是把 DES 跑了三次。它用两个或三个密钥,执行“加密-解密-加密”的流程。为什么中间是解密?这是为了兼容性——如果三个密钥相同,3DES 就退化成单 DES。我当年做系统迁移时,就靠这个特性让新旧系统无缝对接了一段时间。

3DES 有三种模式:

模式 密钥长度 安全强度 典型场景
DES-EEE3 3 个独立密钥(168 位) 金融交易
DES-EDE3 3 个独立密钥(168 位) EMV 芯片卡
DES-EDE2 2 个独立密钥(112 位) 遗留系统兼容

你可能会问:为什么不用 3 个密钥直接加密三次?因为 EDE 模式(加密-解密-加密)有个好处:如果三个密钥相同,加密和解密就抵消了,结果等于单 DES。这在系统升级时特别有用——先让所有设备用同一个密钥跑单 DES,再逐步切换到 3DES。

我的经验:做 3DES 兼容性测试时,一定要验证“单密钥退化”这个特性。我曾经遇到一个设备,厂商说支持 3DES,结果三个密钥设成一样时,加密结果和单 DES 对不上——原来是他们自己魔改了算法。这种坑,踩一次就够了。

C 语言实现:从零写一个 DES 核心

我不会让你从零写完整 DES,那太折磨人了。但理解核心的 S 盒替换和 Feistel 结构,对后续安全编程很有帮助。下面是一个简化版的 DES 轮函数实现,重点展示 S 盒替换的逻辑:

#include <stdint.h>
#include <string.h>

// 8 个 S 盒(每个 4x16,这里只展示 S1 作为示例)
static const uint8_t sbox[8][4][16] = {
    {   // S1
        {14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7},
        {0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},
        {4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},
        {15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}
    }
    // 实际应有 S2~S8,此处省略
};

// 6 位输入 -> 4 位输出
uint8_t sbox_lookup(int box_index, uint8_t input_6bit) {
    // 行:由首尾两位决定(bit0 和 bit5)
    uint8_t row = ((input_6bit & 0x20) >> 4) | (input_6bit & 0x01);
    // 列:由中间 4 位决定(bit1~bit4)
    uint8_t col = (input_6bit >> 1) & 0x0F;
    return sbox[box_index][row][col];
}

// 简化版 Feistel 轮函数(仅演示 S 盒部分)
uint32_t feistel(uint32_t half_block, uint64_t subkey) {
    // 实际 DES 中,这里要先做扩展置换(E 盒)
    // 然后与子密钥异或,再经过 8 个 S 盒,最后 P 盒置换
    // 这里仅做概念演示
    uint8_t input = (uint8_t)(half_block & 0x3F);  // 取低 6 位
    uint8_t output = sbox_lookup(0, input);        // 用 S1 盒
    return (uint32_t)output;
}

int main() {
    // 测试:输入 0x3F(二进制 111111),行=11(3),列=1111(15)
    uint8_t result = sbox_lookup(0, 0x3F);
    // 查表 S1[3][15] = 13(0x0D)
    printf("S1(0x3F) = 0x%X\n", result);
    return 0;
}

这段代码虽然简单,但抓住了 S 盒替换的精髓。实际 DES 的轮函数要复杂得多,包括 48 位的扩展置换、与 48 位子密钥的异或、8 个 S 盒并行、32 位 P 盒置换。但核心思想不变:混淆 + 扩散

警告:千万不要在生产环境中使用自己写的 DES 实现。密码学实现有太多侧信道攻击的坑——时序攻击、缓存攻击、功耗分析。我见过一个团队自己实现了 DES,结果因为 S 盒查表时间不一致,被攻击者通过网络延迟就还原出了密钥。用 OpenSSL 或 mbedTLS 这类经过审计的库,才是正道。

知识体系总览

下面这张图,是我梳理的 DES/3DES 知识结构。你可以把它当作学习路线图:

DES/3DES 知识体系 对称加密(DES/3DES) DES 算法原理 初始置换(IP) 16 轮 Feistel 迭代 逆初始置换(IP⁻¹) 3DES 兼容性 EDE 模式(加密-解密-加密) 单密钥退化兼容 EEE3 / EDE3 / EDE2 C 语言实现要点 S 盒查表实现 Feistel 轮函数 密钥调度(子密钥生成) ⚠ 安全提示:生产环境请使用 OpenSSL / mbedTLS 等经过审计的库 DES 核心:56位密钥 + 16轮 Feistel + 8个S盒 | 3DES 核心:三次DES + EDE兼容模式

这张图把 DES 原理、3DES 兼容性、C 语言实现要点串在了一起。你可以看到,DES 的核心是 16 轮 Feistel 结构和 8 个 S 盒;3DES 的核心是 EDE 模式和单密钥退化;C 语言实现则要重点关注 S 盒查表和密钥调度。

最后说一句:虽然 DES 和 3DES 正在被逐步淘汰,但理解它们对学习 AES、SM4 等现代对称加密非常有帮助。很多设计思想——比如 Feistel 结构、S 盒非线性替换、密钥扩展——都是一脉相承的。你把这些吃透了,后面学 AES 会轻松很多。

一句话总结:DES 是 56 位密钥的 Feistel 密码,3DES 是三次 DES 的兼容方案。C 语言实现时,S 盒查表是核心,但生产环境务必使用成熟库。


公众号:蓝海资料掘金营,微信deep3321