分组密码基础(二):Feistel 结构与 DES 的设计思想
-
- 前言
- [一、Feistel 结构:分组密码的经典范式](#一、Feistel 结构:分组密码的经典范式)
-
- [1.1 从"单一大置换"到多轮迭代](#1.1 从"单一大置换"到多轮迭代)
- [1.2 Feistel 网络的基本结构](#1.2 Feistel 网络的基本结构)
-
- [1.2.1 单轮变换规则](#1.2.1 单轮变换规则)
- [1.2.2 轮函数 F](#1.2.2 轮函数 F)
- [1.2.3 多轮迭代与最终输出](#1.2.3 多轮迭代与最终输出)
- [1.3 为什么 Feistel 是置换?](#1.3 为什么 Feistel 是置换?)
-
- [1.3.1 证明单射(不会碰撞)](#1.3.1 证明单射(不会碰撞))
- [1.3.2 证明满射(都能解密)](#1.3.2 证明满射(都能解密))
- [1.4 核心性质:加解密对称性证明](#1.4 核心性质:加解密对称性证明)
-
- [1.4.1 单轮的逆变换](#1.4.1 单轮的逆变换)
- [1.4.2 多轮的逆变换](#1.4.2 多轮的逆变换)
- [1.4.3 一个极其重要的推论](#1.4.3 一个极其重要的推论)
- [1.5 Feistel 结构的优势与局限](#1.5 Feistel 结构的优势与局限)
- [二、DES 算法概览](#二、DES 算法概览)
-
- [2.1 诞生与标准化历程](#2.1 诞生与标准化历程)
- [2.2 整体加密流程](#2.2 整体加密流程)
- [2.2 整体加密流程](#2.2 整体加密流程)
- [三、初始置换 IP 与逆初始置换](#三、初始置换 IP 与逆初始置换)
-
- [3.1 IP 置换表与查表规则](#3.1 IP 置换表与查表规则)
- [3.2 逆初始置换 IP⁻¹](#3.2 逆初始置换 IP⁻¹)
- [3.3 设计意图与争议](#3.3 设计意图与争议)
- [四、DES 轮函数深度拆解](#四、DES 轮函数深度拆解)
-
- [4.1 轮函数整体结构与 E 扩展](#4.1 轮函数整体结构与 E 扩展)
- [4.2 S 盒代换:DES 的非线性核心](#4.2 S 盒代换:DES 的非线性核心)
-
- [4.2.1 查表规则(标准固定拆分方式)](#4.2.1 查表规则(标准固定拆分方式))
- [4.2.2 S 1 S_1 S1 查表实操](#4.2.2 S 1 S_1 S1 查表实操)
- [4.2.3 S盒硬性设计约束](#4.2.3 S盒硬性设计约束)
- [4.2.4 S盒与差分抗性(攻击视角)](#4.2.4 S盒与差分抗性(攻击视角))
- [4.3 P 置换与一轮完整流程](#4.3 P 置换与一轮完整流程)
-
- [4.3.1 P 置换表](#4.3.1 P 置换表)
- [4.3.2 一轮完整流程走查](#4.3.2 一轮完整流程走查)
- 五、子密钥生成算法
-
- [5.1 PC-1 置换:64 位到 56 位的密钥压缩](#5.1 PC-1 置换:64 位到 56 位的密钥压缩)
- [5.2 循环左移:为什么每轮移位数不同?](#5.2 循环左移:为什么每轮移位数不同?)
- [5.3 PC-2 置换:56 位到 48 位的子密钥选择](#5.3 PC-2 置换:56 位到 48 位的子密钥选择)
- [5.4 16 轮子密钥完整生成流程](#5.4 16 轮子密钥完整生成流程)
- [六、多重DES:DES 的兼容改进方案](#六、多重DES:DES 的兼容改进方案)
-
- [6.1 双重 DES(2DES)](#6.1 双重 DES(2DES))
- [6.2 三重 DES(3DES / TDEA)](#6.2 三重 DES(3DES / TDEA))
- [6.3 3DES 的优劣与现状](#6.3 3DES 的优劣与现状)
- [七、总结:Feistel 结构与 DES 的历史遗产](#七、总结:Feistel 结构与 DES 的历史遗产)
- 免责声明
前言
在上一篇「分组密码基础(一)」中,我们建立了分组密码的基础框架:理解了为什么要分组、轮函数的核心地位,以及 ECB / CBC / CTR 等工作模式如何让定长置换处理任意长度的消息。
但有一个核心问题还没有回答:轮函数内部该怎么设计?一个安全的分组密码遵循什么样的设计原则?
现代分组密码的轮函数设计,最终收敛到了两条经典路线:
- Feistel 结构 :以 DES 为代表,将明文拆为左右两半,轮函数只作用于一半,实现了加解密几乎完全对称的优美性质。
- SPN 结构:以 AES 为代表,通过 S 盒(混淆)+ P 盒(扩散)的交替迭代,在整个分组上直接施加非线性变换。
这两种结构是现代分组密码的两大基石。理解了它们,再看 Blowfish、Twofish、SM4 等任何算法,都能迅速抓住核心逻辑。
本篇我们聚焦 Feistel 结构,并以 DES 为完整案例深度拆解。我们不会停留在"DES 有哪些步骤"的表面,而是追问每一个设计选择背后的原因------知其然,更要知其所以然。
Crypto专栏:https://blog.csdn.net/r_feynman_/category_13190241.html
Crypto 密码解析实战靶场:https://blog.csdn.net/r_feynman_/category_13194584.html
一、Feistel 结构:分组密码的经典范式
1.1 从"单一大置换"到多轮迭代
分组密码的数学本质很简单:它是一个 n n n 比特空间上的置换------每个明文唯一对应一个密文,反之亦然。
但问题来了:为什么不直接设计一个"足够乱"的大置换,查表加密就行了?
原因有两个。第一,存不下 。64 位分组的置换表要有 2 64 2^{64} 264 条记录,这是个天文数字,根本不可能实现。第二,没法分析安全性。一个黑盒般的大置换,你根本不知道它有没有隐藏的弱点、有没有数学规律可以被利用。
所以密码学家换了个思路:不搞一个大置换,而是搞很多轮"小操作"。每一轮只做一点点变换,看起来很简单,但多轮叠加之后,整体就变成了一个极其复杂的置换。
这就是迭代分组密码 的核心思想。每一轮的内部运算逻辑,就叫轮函数。
迭代结构的好处很明显:每一轮操作都很简单,便于分析安全性;实现起来也高效,不需要巨大的存储空间。而 Feistel 结构,就是迭代分组密码中最经典、影响最深远的一种设计框架。
1.2 Feistel 网络的基本结构
Feistel 结构(也叫 Feistel 网络 / Feistel 密码)由 IBM 密码学家 Horst Feistel 在 1970 年代提出,是分组密码史上最经典的设计框架之一,后来成为美国数据加密标准 DES 的核心架构。
它的核心设计思路非常巧妙:不直接对整个分组做变换,而是把明文分组平均切成左右两半,每一轮只"搅乱"其中一半,另一半直接平移过去。
这种"半分组处理"的设计,正是 Feistel 结构所有优美性质的根源。
1.2.1 单轮变换规则
具体来说,设分组总长度为 2 n 2n 2n 位,左右两半各 n n n 位。初始明文被拆分为左半部分 L 0 L_0 L0 和右半部分 R 0 R_0 R0。
随后进行 r r r 轮迭代,每一轮的操作规则完全相同:
L i = R i − 1 R i = L i − 1 ⊕ F ( R i − 1 , K i ) \begin{align*} L_i &= R_{i-1} \\ R_i &= L_{i-1} \oplus F(R_{i-1}, K_i) \end{align*} LiRi=Ri−1=Li−1⊕F(Ri−1,Ki)
逐行解释:
- 第一行 L i = R i − 1 L_i = R_{i-1} Li=Ri−1:上一轮的右半部分,直接变成这一轮的左半部分。简单说就是"右边的直接移到左边",不做任何变换。
- 第二行 R i = L i − 1 ⊕ F ( R i − 1 , K i ) R_i = L_{i-1} \oplus F(R_{i-1}, K_i) Ri=Li−1⊕F(Ri−1,Ki):上一轮的左半部分,与轮函数的输出做异或运算,结果作为这一轮的右半部分。

1.2.2 轮函数 F
公式中的 F F F 就是轮函数(Round Function),它是 Feistel 结构的核心组件,也是密码安全性的主要来源。
轮函数接受两个输入:
- 上一轮的右半部分 R i − 1 R_{i-1} Ri−1
- 第 i i i 轮的轮子密钥 K i K_i Ki(由主密钥通过密钥调度算法生成)
轮函数的输出长度与左半部分相同,都是 n n n 位,这样才能与左半部分做异或运算。
特别注意 :轮函数 F F F 不需要是可逆的。这是 Feistel 结构极其重要的一个性质------整个密码的可逆性由异或操作和左右分片机制保证,轮函数本身可以设计得任意复杂、任意非线性。我们会在 1.4 节详细证明这一点。
1.2.3 多轮迭代与最终输出
r r r 轮迭代全部完成之后,将最后一轮的左右两半拼接起来,就得到了密文:
密文 = L r ∥ R r \text{密文} = L_r \ \|\ R_r 密文=Lr ∥ Rr
其中 ∥ \| ∥ 表示比特串拼接。
💡 一个容易忽略的细节:通用理论Feistel模型迭代完成后输出 L r ∥ R r L_r\|R_r Lr∥Rr;DES执行完16轮迭代得到 L 16 ∥ R 16 L_{16}\|R_{16} L16∥R16,迭代内部不再执行交换,我们需要手动交换左右两半得到 R 16 ∥ L 16 R_{16}\|L_{16} R16∥L16 ,再送入 I P − 1 IP^{-1} IP−1。
这个设计保证解密仅需倒序使用子密钥,其余运算逻辑完全复用。若忽视这次手动交换,算法实现将直接出错。
1.3 为什么 Feistel 是置换?
在讲解密之前,我们先确认一个最基本的问题:Feistel 结构加密出来的结果,能不能唯一解密回去?
换句话说,加密操作是不是一个置换(Permutation)?如果不是,两个不同的明文可能加密成同一个密文,那就乱套了------解密的时候你根本不知道该还原成哪个。
答案是:是的,每一轮 Feistel 变换都是置换,因此整个加密过程也是置换。
我们来简单证明一下。
1.3.1 证明单射(不会碰撞)
设单轮 Feistel 变换为函数 F K i \mathcal{F}{K_i} FKi,输入为第 i − 1 i-1 i−1 轮状态 ( L i − 1 , R i − 1 ) (L{i-1}, R_{i-1}) (Li−1,Ri−1),输出为第 i i i 轮状态 ( L i , R i ) (L_i,R_i) (Li,Ri):
F K i ( L i − 1 , R i − 1 ) = ( L i , R i ) = ( R i − 1 , L i − 1 ⊕ F ( R i − 1 , K i ) ) \mathcal{F}{K_i}(L{i-1}, R_{i-1}) = \big(L_i,\; R_i\big) = \bigl(R_{i-1},\ L_{i-1} \oplus F(R_{i-1}, K_i)\bigr) FKi(Li−1,Ri−1)=(Li,Ri)=(Ri−1, Li−1⊕F(Ri−1,Ki))
现在证明:如果两个输入经过变换后输出相同,那么输入一定相同。
假设:
F K i ( L 1 , R 1 ) = F K i ( L 2 , R 2 ) \mathcal{F}{K_i}(L_1, R_1) = \mathcal{F}{K_i}(L_2, R_2) FKi(L1,R1)=FKi(L2,R2)
根据函数定义,输出左分量等于输入右分量,因此:
R 1 = R 2 (1) R_1 = R_2 \tag{1} R1=R2(1)
输出右分量等于输入左分量异或轮函数输出:
L 1 ⊕ F ( R 1 , K i ) = L 2 ⊕ F ( R 2 , K i ) (2) L_1 \oplus F(R_1, K_i) = L_2 \oplus F(R_2, K_i) \tag{2} L1⊕F(R1,Ki)=L2⊕F(R2,Ki)(2)
由式 (1) 可知 R 1 = R 2 R_1 = R_2 R1=R2,推得 F ( R 1 , K i ) = F ( R 2 , K i ) F(R_1, K_i) = F(R_2, K_i) F(R1,Ki)=F(R2,Ki)。
式 (2) 两边同时异或 F ( R 1 , K i ) F(R_1, K_i) F(R1,Ki),得到:
L 1 = L 2 L_1 = L_2 L1=L2
结合式 (1),有 ( L 1 , R 1 ) = ( L 2 , R 2 ) (L_1, R_1) = (L_2, R_2) (L1,R1)=(L2,R2)。
单射得证------不会出现"两个不同明文加密成同一个密文"的碰撞情况。
1.3.2 证明满射(都能解密)
反过来,证明:对于任意一个输出,都一定能找到对应的输入。
任取输出 ( L ′ , R ′ ) (L', R') (L′,R′),我们构造输入如下:
{ R i − 1 = L ′ L i − 1 = R ′ ⊕ F ( L ′ , K i ) \begin{cases} R_{i-1} = L' \\ L_{i-1} = R' \oplus F(L', K_i) \end{cases} {Ri−1=L′Li−1=R′⊕F(L′,Ki)
现在验证这个输入经过 F K i \mathcal{F}_{K_i} FKi 变换后,确实能得到 ( L ′ , R ′ ) (L', R') (L′,R′)。
输出左半部分:
L i = R i − 1 = L ′ ✓ L_i = R_{i-1} = L' \quad \text{✓} Li=Ri−1=L′✓
输出右半部分:
R i = L i − 1 ⊕ F ( R i − 1 , K i ) = ( R ′ ⊕ F ( L ′ , K i ) ) ⊕ F ( L ′ , K i ) = R ′ ⊕ ( F ( L ′ , K i ) ⊕ F ( L ′ , K i ) ) = R ′ ⊕ 0 = R ′ ✓ \begin{align*} R_i &= L_{i-1} \oplus F(R_{i-1}, K_i) \\ &= \bigl(R' \oplus F(L', K_i)\bigr) \oplus F(L', K_i) \\ &= R' \oplus \bigl(F(L', K_i) \oplus F(L', K_i)\bigr) \\ &= R' \oplus 0 \\ &= R' \quad \text{✓} \end{align*} Ri=Li−1⊕F(Ri−1,Ki)=(R′⊕F(L′,Ki))⊕F(L′,Ki)=R′⊕(F(L′,Ki)⊕F(L′,Ki))=R′⊕0=R′✓
(异或的性质: x ⊕ x = 0 x \oplus x = 0 x⊕x=0, x ⊕ 0 = x x \oplus 0 = x x⊕0=x)
所以 F K i ( L i − 1 , R i − 1 ) = ( L ′ , R ′ ) \mathcal{F}{K_i}(L{i-1}, R_{i-1}) = (L', R') FKi(Li−1,Ri−1)=(L′,R′)。
满射得证------任何密文都一定能解密回唯一的明文。
既是单射又是满射,那就是双射,也就是置换。
每一轮都是置换, r r r 轮复合之后,整个加密函数也是置换:
E = F K r ∘ F K r − 1 ∘ ⋯ ∘ F K 1 \mathcal{E} = \mathcal{F}{K_r} \circ \mathcal{F}{K_{r-1}} \circ \cdots \circ \mathcal{F}_{K_1} E=FKr∘FKr−1∘⋯∘FK1
这就从数学上保证了解密一定是可行的。
1.4 核心性质:加解密对称性证明
Feistel 结构最巧妙、最优美的性质,就是加解密几乎完全一样。
加密和解密使用同一套运算结构,唯一区别是子密钥倒序使用。
该性质工程价值极高:硬件实现无需设计两套独立电路;软件实现加密、解密可复用绝大多数代码。下面完成数学证明。
1.4.1 单轮的逆变换
正向单轮 Feistel 变换:
L i = R i − 1 R i = L i − 1 ⊕ F ( R i − 1 , K i ) \begin{align*} L_i &= R_{i-1} \\ R_i &= L_{i-1} \oplus F(R_{i-1}, K_i) \end{align*} LiRi=Ri−1=Li−1⊕F(Ri−1,Ki)
已知输出状态 ( L i , R i ) (L_i,R_i) (Li,Ri),求解上一轮状态 ( L i − 1 , R i − 1 ) (L_{i-1},R_{i-1}) (Li−1,Ri−1)。
由第一式直接得到: R i − 1 = L i R_{i-1}=L_i Ri−1=Li,代入第二式:
R i = L i − 1 ⊕ F ( L i , K i ) R_i = L_{i-1} \oplus F(L_i, K_i) Ri=Li−1⊕F(Li,Ki)
等式两侧同时异或 F ( L i , K i ) F(L_i,K_i) F(Li,Ki):
L i − 1 = R i ⊕ F ( L i , K i ) L_{i-1} = R_i \oplus F(L_i, K_i) Li−1=Ri⊕F(Li,Ki)
得到单轮逆变换:
L i − 1 = R i ⊕ F ( L i , K i ) R i − 1 = L i \begin{align*} L_{i-1} &= R_i \oplus F(L_i, K_i) \\ R_{i-1} &= L_i \end{align*} Li−1Ri−1=Ri⊕F(Li,Ki)=Li
对比正向变换与逆变换:二者运算结构高度相似,等效于将分组左右两半交换后再执行 Feistel 迭代。
1.4.2 多轮的逆变换
设加密共 r r r 轮,加密子密钥序列: K 1 , K 2 , ... , K r K_1,K_2,\dots,K_r K1,K2,...,Kr,输入明文 ( L 0 , R 0 ) (L_0,R_0) (L0,R0),输出密文 ( L r , R r ) (L_r,R_r) (Lr,Rr)。
解密规则:使用逆序子密钥 K r , K r − 1 , ... , K 1 K_r,K_{r-1},\dots,K_1 Kr,Kr−1,...,K1,调用完全相同的 Feistel 迭代函数;解密迭代前,先将密文交换左右分组,以 ( R r , L r ) (R_r,L_r) (Rr,Lr) 作为解密初始输入。

考察解密第1轮(抵消加密第 r r r 轮):
解密输入 ( R r , L r ) (R_r,L_r) (Rr,Lr),子密钥 K r K_r Kr,执行正向Feistel规则:
{ L ′ = L r R ′ = R r ⊕ F ( L r , K r ) \begin{cases} L' = L_r \\ R' = R_r \oplus F(L_r, K_r) \end{cases} {L′=LrR′=Rr⊕F(Lr,Kr)
加密第 r r r 轮满足:
{ L r = R r − 1 R r = L r − 1 ⊕ F ( R r − 1 , K r ) \begin{cases} L_r = R_{r-1} \\ R_r = L_{r-1} \oplus F(R_{r-1}, K_r) \end{cases} {Lr=Rr−1Rr=Lr−1⊕F(Rr−1,Kr)
将 L r = R r − 1 L_r=R_{r-1} Lr=Rr−1 代入解密输出右分量:
R ′ = ( L r − 1 ⊕ F ( R r − 1 , K r ) ) ⊕ F ( R r − 1 , K r ) = L r − 1 R' = \big(L_{r-1} \oplus F(R_{r-1}, K_r)\big) \oplus F(R_{r-1}, K_r) = L_{r-1} R′=(Lr−1⊕F(Rr−1,Kr))⊕F(Rr−1,Kr)=Lr−1
解密一轮输出状态为 ( L r , L r − 1 ) = ( R r − 1 , L r − 1 ) (L_r, L_{r-1})=(R_{r-1},L_{r-1}) (Lr,Lr−1)=(Rr−1,Lr−1)。
可见:每执行一轮解密迭代,即可抵消一轮加密变换,但分组左右保持倒置。以此递推,全部 r r r 轮解密迭代完成后,输出状态为 ( R 0 , L 0 ) (R_0,L_0) (R0,L0)。
最后交换左右分组,恢复原始明文 ( L 0 , R 0 ) (L_0,R_0) (L0,R0)。
证毕。
1.4.3 一个极其重要的推论
整个证明过程中,并未对轮函数 F F F 施加可逆要求。
Feistel 结构整体可逆性依托异或运算实现,与轮函数是否为双射无关。轮函数可以任意构造,无需考虑逆运算实现。
这给予密码设计者极大自由度:可以在轮函数中大量引入非线性组件(S盒、复杂置换等),专注提升密码抗攻击能力。这也是 Feistel 结构能够广泛流行的关键原因。
1.5 Feistel 结构的优势与局限
| 优势 | 说明 |
|---|---|
| ① 加解密高度对称 | 加密与解密复用同一套结构,仅子密钥倒序使用,硬件/软件实现成本低 |
| ② 轮函数无需可逆 | 整体可逆性由异或运算保障,轮函数可自由设计,便于引入非线性组件 |
| ③ 安全性分析成熟 | 差分、线性密码分析等经典方法均基于 Feistel 框架发展,评估体系完善 |
| 局限 | 说明 |
|---|---|
| ① 每轮仅处理半部分组 | 左半分组直接平移,单比特扰动需经过两轮才能传播至全部分组 |
| ② 扩散速度较慢 | 半分组处理机制导致扩散效率低于 SPN 结构,达到同等安全强度通常需要更多轮数 |
Feistel 结构简洁优雅,深刻影响了现代分组密码发展。DES 作为其最经典的实现,是密码学史上的里程碑。接下来我们以 DES 为案例,完整拆解 Feistel 密码的工程落地与设计思想。
二、DES 算法概览
DES(Data Encryption Standard,数据加密标准)是密码学史上第一个被广泛应用的民用标准化分组密码,它的诞生标志着现代密码学从军事、外交领域正式走向商业和民用。
2.1 诞生与标准化历程
故事始于 1973 年,美国国家标准局 NBS(即后来的 NIST)公开征集加密标准,要求算法公开、安全可靠、易于实现,以解决当时民间加密算法各自为政、互不兼容的问题。
IBM 公司响应了征集,他们提交了一个内部项目 Lucifer 的改进版本------由 Horst Feistel 领导开发,采用的正是我们上一章讲的 Feistel 结构。
经过 NSA 参与修改(主要是 S 盒设计和密钥长度调整)后,算法于 1977 年 正式被采纳为联邦标准,命名为 DES。
DES 一用就是二十多年,银行、金融、通信、政府......到处都能看到它的身影。但随着计算机算力的飞速提升,56 位密钥逐渐显得力不从心。1998 年,电子前沿基金会 EFF 用专用机器"Deep Crack"仅用 56 小时就暴力破解了 DES。
2001 年,NIST 正式宣布 AES 取代 DES,成为新的加密标准。不过 DES 的衍生版本 3DES(三重 DES)又沿用了很多年,直到 2023 年才被正式弃用。
💡 关于 NSA 修改 S 盒的争议:当年很多人怀疑 NSA 在 S 盒里留了后门。但后来差分密码分析公开后,人们发现修改后的 S 盒反而增强了抗差分攻击的能力------也就是说,NSA 当年其实是在帮 DES 变得更安全,只是他们早就知道差分攻击这种方法,而学术界还不知道。
2.2 整体加密流程
2.2 整体加密流程
DES 是一个典型的 Feistel 结构分组密码,整体流程如下:

| 明文加密链路(左侧) | 密钥生成链路(右侧) |
|---|---|
| 64 位明文输入 | 64 位主密钥输入 |
| IP 初始置换打乱位置 | PC-1 置换压缩到 56 位 |
| 拆成左右两半 L 0 L_0 L0、 R 0 R_0 R0,各 32 位 | 拆成 C 0 C_0 C0、 D 0 D_0 D0 两半,各 28 位 |
| 16 轮 Feistel 迭代,每轮使用对应子密钥 | 每轮循环左移不同位数 |
| 16 轮结束后拼接左右两半 | PC-2 置换选出 48 位,生成当前轮子密钥 |
| 逆初始置换 IP⁻¹,输出 64 位密文 | 共生成 16 个 48 位子密钥 |
DES 是一个典型的 Feistel 结构分组密码,整个加密过程分三步:
- 初始置换 IP:64 位明文按固定表重排位置
- 16 轮 Feistel 迭代:核心部分,每轮使用一个 48 位子密钥
- 逆初始置换 IP⁻¹:把 IP 打乱的位置还原,输出 64 位密文
16 轮迭代遵循标准 Feistel 规则:
L i = R i − 1 R i = L i − 1 ⊕ F ( R i − 1 , K i ) \begin{align*} L_i &= R_{i-1} \\ R_i &= L_{i-1} \oplus F(R_{i-1}, K_i) \end{align*} LiRi=Ri−1=Li−1⊕F(Ri−1,Ki)
其中轮函数 F F F 是 DES 最复杂的部分,内部包含 E 扩展盒、S 盒代换、P 置换三个核心组件,我们后面第四章专门拆解。
text
明文(64位) → IP → 16轮Feistel迭代 → IP⁻¹ → 密文(64位)
核心参数
| 参数 | 长度 | 说明 |
|---|---|---|
| 分组长度 | 64 位 | 每次加密处理 8 字节数据 |
| 密钥长度 | 64 位(有效 56 位) | 其中 8 位为奇偶校验位,不参与加密运算 |
| 迭代轮数 | 16 轮 | 安全性与实现效率的平衡点 |
- 64 位分组:在 70 年代是兼顾安全与效率的合理选择,以今天的标准看略短,易受生日攻击等威胁
- 56 位有效密钥:当年足够安全,随摩尔定律推进,暴力破解成本急剧下降,最终成为 DES 退役的直接原因
- 16 轮迭代 :经过精心选择。差分攻击攻破 16 轮 DES 约需 2 47 2^{47} 247 个选择明文,已接近暴力破解的 2 56 2^{56} 256 复杂度,再增加轮数安全收益递减
三、初始置换 IP 与逆初始置换
初始置换 IP(Initial Permutation)是 DES 加密的第一步,逆初始置换 IP⁻¹ 是最后一步。这两步操作只换位、不改值,也不涉及密钥,因此很多人刚学 DES 时都会疑惑:这两步到底有啥用?不是多此一举吗?
我们先看它是怎么工作的,再说设计意图。
3.1 IP 置换表与查表规则
初始置换就是一张固定的 64 位置换表,把输入的 64 位数据按表重新排列位置。
先记牢统一读法(所有置换表都适用):
- 表的位置 = 输出第几位(从上到下、从左到右依次是第 1、2、3......位)
- 表中的数值 = 取输入的第几位
IP 置换表如下:
| 58 | 50 | 42 | 34 | 26 | 18 | 10 | 2 |
|---|---|---|---|---|---|---|---|
| 60 | 52 | 44 | 36 | 28 | 20 | 12 | 4 |
| 62 | 54 | 46 | 38 | 30 | 22 | 14 | 6 |
| 64 | 56 | 48 | 40 | 32 | 24 | 16 | 8 |
| 57 | 49 | 41 | 33 | 25 | 17 | 9 | 1 |
| 59 | 51 | 43 | 35 | 27 | 19 | 11 | 3 |
| 61 | 53 | 45 | 37 | 29 | 21 | 13 | 5 |
| 63 | 55 | 47 | 39 | 31 | 23 | 15 | 7 |
输出位序号计算公式:
输出位 = ( 行号 − 1 ) × 8 + 列号 \text{输出位} = (\text{行号} - 1) \times 8 + \text{列号} 输出位=(行号−1)×8+列号
举两个例子:
例 1:第 1 行第 1 列
- 输出位 = (1 - 1) × 8 + 1 = 第 1 位
- 数值 = 58
- 含义:输出第 1 位 = 输入第 58 位
例 2:第 5 行第 8 列
- 输出位 = (5 - 1) × 8 + 8 = 第 40 位
- 数值 = 1
- 含义:输出第 40 位 = 输入第 1 位
简单说就是:把输入的 64 位按这张表重新排个队。
💡 注意:DES 标准中位编号从 1 开始,写代码时要转换成 0 基索引,别搞混了。
3.2 逆初始置换 IP⁻¹
逆初始置换就是 IP 的逆操作------IP 把位置打乱,IP⁻¹ 再按逆表换回去。如果先做 IP 再做 IP⁻¹,数据就完全恢复原样。
IP⁻¹ 置换表如下(读法和 IP 表完全一样):
| 40 | 8 | 48 | 16 | 56 | 24 | 64 | 32 |
|---|---|---|---|---|---|---|---|
| 39 | 7 | 47 | 15 | 55 | 23 | 63 | 31 |
| 38 | 6 | 46 | 14 | 54 | 22 | 62 | 30 |
| 37 | 5 | 45 | 13 | 53 | 21 | 61 | 29 |
| 36 | 4 | 44 | 12 | 52 | 20 | 60 | 28 |
| 35 | 3 | 43 | 11 | 51 | 19 | 59 | 27 |
| 34 | 2 | 42 | 10 | 50 | 18 | 58 | 26 |
| 33 | 1 | 41 | 9 | 49 | 17 | 57 | 25 |
IP 表和 IP⁻¹ 表是严格互逆的,满足:
如果 IP 中输出第 i 位 = 输入第 j 位,那么 IP⁻¹ 中输出第 j 位 = 输入第 i 位。
用数学语言写就是:
IP i = j ⟺ IP − 1 j = i \text{IP}i = j \iff \text{IP}^{-1}j = i IPi=j⟺IP−1j=i
我们来完整验证一个:
第一步:从 IP 表找
- 第 1 行第 1 列 → 输出第 1 位 → 值为 58
- 即: IP 1 = 58 \text{IP}1 = 58 IP1=58
- 含义:输出第 1 位取输入第 58 位
第二步:从 IP⁻¹ 表验证
- 互逆的话,应该有 IP − 1 58 = 1 \text{IP}^{-1}58 = 1 IP−158=1
- 也就是:输出第 58 位取输入第 1 位
- 算位置:输出第 58 位 = 第几行第几列?
- 行号 = ⌊(58 - 1) / 8⌋ + 1 = ⌊57 / 8⌋ + 1 = 7 + 1 = 第 8 行
- 列号 = (58 - 1) % 8 + 1 = 57 % 8 + 1 = 1 + 1 = 第 2 列
- 第 8 行第 2 列的值是 1
- 即: IP − 1 58 = 1 ✓ \text{IP}^{-1}58 = 1 \quad ✓ IP−158=1✓
完全对得上。
⚠️ 常见坑提醒:别把"格子里的数字"和"输出位序号"搞混了。格子里写的 58 是输入位,不是输出位。先定位到输出第几位,再读格子里的值。
3.3 设计意图与争议
初始置换是 DES 最受争议的设计之一。很多人觉得它只换位、不改值,也不涉及密钥,纯属多余。
关于它的作用,主要有两种说法:
① 硬件实现的工程优化
这是比较主流的观点。DES 设计年代以硬件实现为主,初始置换把同一个字节的 8 位打散到不同位置,让后续轮函数的数据分布更"散",硬件布线更方便。
简单说:不是为了安全,是为了工程实现便利。
② 增加软件实现难度
另一种说法:NSA 要求加这一步,是为了抬高纯软件实现 DES 的门槛。初始置换打乱字节边界,软件实现要做大量位操作,效率不高;而硬件实现就是走几根线,几乎零成本。
从密码分析角度看,初始置换不会提升算法理论安全强度 。攻击者只需在分析前预先执行逆置换,即可完全抵消IP带来的比特重排效果。正因如此,AES、SM4等现代分组密码,都取消了这种首尾无密钥参与的固定置换。
不管怎样,初始置换是 DES 的标准组成部分,了解即可。接下来进入 DES 的核心------轮函数。
四、DES 轮函数深度拆解
轮函数 F F F 是 DES 安全性的核心,它接受 32 位右半分组 R i − 1 R_{i-1} Ri−1 和 48 位子密钥 K i K_i Ki,输出 32 位结果。
内部完整结构如下:

从左到右看,整个轮函数分四步:
- E 扩展(Expansion):把 32 位输入扩展成 48 位
- 密钥异或:48 位数据与 48 位子密钥按位异或
- S 盒代换(Substitution):48 位分成 8 组,每组 6 位,经 8 个 S 盒各输出 4 位,共 32 位
- P 置换(Permutation):32 位按固定表重排位置
用公式表示:
R i − 1 → E扩展 48 位 → ⊕ K i 48 位 → S盒代换 32 位 → P置换 32 位 R_{i-1} \xrightarrow{\text{E扩展}} 48\text{位} \xrightarrow{\oplus K_i} 48\text{位} \xrightarrow{\text{S盒代换}} 32\text{位} \xrightarrow{\text{P置换}} 32\text{位} Ri−1E扩展 48位⊕Ki 48位S盒代换 32位P置换 32位
输出结果与左半分组 L i − 1 L_{i-1} Li−1 异或,得到新一轮右半分组 R i R_i Ri。
我们逐个拆解。
4.1 轮函数整体结构与 E 扩展
轮函数输入 :32 位右半分组 R i − 1 R_{i-1} Ri−1 + 48 位子密钥 K i K_i Ki
轮函数输出:32 位
完整四步:
- E 扩展:32 位 → 48 位(对齐子密钥长度 + 加速扩散)
- 密钥异或:48 位数据与 48 位子密钥按位异或
- S 盒代换:48 位分成 8 组,每组 6 位,经 8 个 S 盒各输出 4 位,共 32 位
- P 置换:32 位按固定表重排位置,进一步扩散
输出结果与左半分组 L i − 1 L_{i-1} Li−1 异或,得到新一轮右半分组 R i R_i Ri。
E 扩展盒详解
E 扩展(Expansion)把 32 位输入扩展为 48 位。看图中轮函数的第一步,就是 Expansion Permutation。
E 扩展表(输出位 → 输入位):
| 32 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 4 | 5 | 6 | 7 | 8 | 9 |
| 8 | 9 | 10 | 11 | 12 | 13 |
| 12 | 13 | 14 | 15 | 16 | 17 |
| 16 | 17 | 18 | 19 | 20 | 21 |
| 20 | 21 | 22 | 23 | 24 | 25 |
| 24 | 25 | 26 | 27 | 28 | 29 |
| 28 | 29 | 30 | 31 | 32 | 1 |
扩展规律:
把 32 位输入从左到右每 4 位分成一组,共 8 组。每组 4 位扩展成 6 位------保留中间 4 位,再从左右邻居各借 1 位。
举个例子:第 1 组是输入第 1~4 位,扩展后变成:
- 左边借 1 位:第 32 位(首尾环形相连)
- 中间 4 位:第 1、2、3、4 位
- 右边借 1 位:第 5 位
所以第 1 组扩展后是:32, 1, 2, 3, 4, 5,共 6 位。
💡 关键细节:因为是首尾相连的环形扩展,每一位输入都会出现在两个输出位置。这样一位变化就能影响两个 S 盒,加速了扩散。
扩展之后的 48 位数据,和 48 位子密钥做异或,然后送入 S 盒。
4.2 S 盒代换:DES 的非线性核心
S盒(Substitution Box,代换盒)是DES唯一的非线性组件 ,也是实现香农「混淆」特性的核心。
如果移除全部S盒,整个加密流程仅剩下比特置换、按位异或这类线性运算,攻击者只需建立线性方程组,就能直接破解密码。
结合轮函数结构图观察:E扩展与子密钥异或后得到48比特数据,从左至右均等切分为8组,每组6比特 ;8组数据依次送入独立的 S 1 ∼ S 8 S_1 \sim S_8 S1∼S8 八个S盒;每个S盒输出4比特,8组输出拼接得到32比特,送入后续P置换。
简单概括:6比特输入查表压缩,得到4比特输出。
4.2.1 查表规则(标准固定拆分方式)
单个S盒由一张 4行×16列 的静态查找表构成,表格内数值范围 0 ∼ 15 0\sim15 0∼15,最终转换为4位二进制作为输出。
设某一组6位输入比特串: b 1 b 2 b 3 b 4 b 5 b 6 \boldsymbol{b_1\ b_2\ b_3\ b_4\ b_5\ b_6} b1 b2 b3 b4 b5 b6
- 行号构造 :取首尾比特 b 1 b 6 b_1b_6 b1b6,拼接为2位二进制,转为十进制(取值区间 0 ∼ 3 0\sim3 0∼3,对应表格4行)
- 列号构造 :取中间4位 b 2 b 3 b 4 b 5 b_2b_3b_4b_5 b2b3b4b5,拼接为4位二进制,转为十进制(取值区间 0 ∼ 15 0\sim15 0∼15,对应表格16列)
- 使用「行号,列号」定位表格数值,补齐为4位二进制,即为S盒输出
⚠️ 极易混淆的编号规则
S盒表格行、列从0开始计数 ;而前文IP初始置换、E扩展表比特位置编号从1开始,两套计数标准不能混用。
完整演算示例
6位输入比特串:1 1 0 1 0 1
- b 1 = 1 , b 6 = 1 b_1=1,\ b_6=1 b1=1, b6=1,二进制
11→ 十进制 3(第3行) - b 2 b 3 b 4 b 5 = 1010 b_2b_3b_4b_5 = \boldsymbol{1010} b2b3b4b5=1010,二进制
1010→ 十进制 10(第10列)
4.2.2 S 1 S_1 S1 查表实操
S 1 S_1 S1 标准表:
| 行\列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 14 | 4 | 13 | 1 | 2 | 15 | 11 | 8 | 3 | 10 | 6 | 12 | 5 | 9 | 0 | 7 |
| 1 | 0 | 15 | 7 | 4 | 14 | 2 | 13 | 1 | 10 | 6 | 12 | 11 | 9 | 5 | 3 | 8 |
| 2 | 4 | 1 | 14 | 8 | 13 | 6 | 2 | 11 | 15 | 12 | 9 | 7 | 3 | 10 | 5 | 0 |
| 3 | 15 | 12 | 8 | 2 | 4 | 9 | 1 | 7 | 5 | 11 | 3 | 14 | 10 | 0 | 6 | 13 |
定位:第3行、第10列,数值 = 3
十进制3补齐4位二进制:0011
S 1 S_1 S1 最终输出:0011
重要知识点: S 1 ∼ S 8 S_1 \sim S_8 S1∼S8 是8张互不相同、预定义的固定常量表,写死在FIPS 46标准中;不存在数学公式可以计算表内数字,只能查表。所有DES程序必须使用完全一致的S盒数据表。
4.2.3 S盒硬性设计约束
8组S盒表格并非随机填充,在标准化时严格满足以下约束:
- 每行是 0 ∼ 15 0\sim15 0∼15 的完整置换
表格任意一行内16个数字互不重复,覆盖0~15全部取值。保证同一输入行下,所有4比特输出均有机会产生。 - 严格雪崩准则(SAC)
输入任意翻转1比特,输出至少改变2比特。实现雪崩效应:输入微小变化,引发输出大范围变动。 - 比特独立性准则(BIC)
任意两个输出比特不存在稳定相关性,攻击者无法依靠部分输出比特推测剩余比特。 - 差分概率上界约束
任意输入差分,无法以极高概率产生固定输出差分;DES单个S盒最大差分概率恰好等于 1 / 4 1/4 1/4,这也是当年NSA刻意约束的指标。
💡 历史插曲
IBM最初Lucifer原型密码的S盒被NSA修改,却未公开修改理由,长期引发「内置后门」猜想。直到差分密码分析在1990年对外发表,学界才证实:NSA调整S盒的目的,正是抵御差分密码分析;差分分析在当时属于美国涉密密码技术,无法公开解释。
4.2.4 S盒与差分抗性(攻击视角)
差分密码分析原理:构造两组具有固定异或差值的明文,观察加密后密文差值的统计学规律,逆向猜测密钥。
S盒是差分攻击最重要的突破口:若S盒频繁出现「固定输入差值 → 高概率固定输出差值」,攻击者就可以构造贯穿多轮的差分路径,大幅降低破解难度。
DES S盒针对性限制差分特征:
- 单个S盒最大差分概率不高于 1 / 4 1/4 1/4
- 经过16轮迭代、P置换扩散后,有效差分路径概率衰减至约 2 − 47 2^{-47} 2−47
- 该复杂度已经十分接近暴力穷举56位密钥的 2 − 56 2^{-56} 2−56,继续增加迭代轮数,安全提升幅度极小。
这也是DES最终选定16轮Feistel迭代的根本原因。
如果你需要,我可以把文字适度精简适配公众号/CSDN排版,或者补齐剩余 S 2 ∼ S 8 S_2\sim S_8 S2∼S8 完整表格附录。
4.3 P 置换与一轮完整流程
S 盒输出的 32 位,还要经过一次 P 置换(Permutation)。看图中轮函数的最后一步,就是 Straight Permutation(P 置换)。
4.3.1 P 置换表
P 置换表(输出位 → 输入位):
| 16 | 7 | 20 | 21 |
|---|---|---|---|
| 29 | 12 | 28 | 17 |
| 1 | 15 | 23 | 26 |
| 5 | 18 | 31 | 10 |
| 2 | 8 | 24 | 14 |
| 32 | 27 | 3 | 9 |
| 19 | 13 | 30 | 6 |
| 22 | 11 | 4 | 25 |
P 置换的作用是扩散 :把每个 S 盒的输出位打散,让一个 S 盒的输出位,在下一轮能影响到多个不同的 S 盒。
具体来说:第 i i i 个 S 盒的 4 个输出位,经过 P 置换后,会被送到 4 个不同的位置;下一轮经过 E 扩展后,这 4 位又会影响到更多的 S 盒。
这样就形成了 E 扩展 → S 盒 → P 置换 的扩散循环:
- E扩展保证任意输入比特会影响两个S盒;
- P置换打散S盒输出,使得单个S盒的输出比特在下一轮扩散至多个不同S盒。
多轮叠加后,一位明文的变化会迅速扩散到整个分组。
4.3.2 一轮完整流程走查
我们结合流程图,把一轮 Feistel 迭代的完整流程串起来:
- 输入 :左半 L i − 1 L_{i-1} Li−1(32 位)、右半 R i − 1 R_{i-1} Ri−1(32 位)、子密钥 K i K_i Ki(48 位)
- E 扩展 : R i − 1 R_{i-1} Ri−1 从 32 位扩展到 48 位
- 密钥异或 :扩展后的 48 位与子密钥 K i K_i Ki 异或
- S 盒代换:48 位分成 8 组,每组 6 位,经 8 个 S 盒代换,输出 32 位
- P 置换:32 位按 P 表重排位置,得到轮函数最终输出
- 异或左半 :轮函数输出与 L i − 1 L_{i-1} Li−1 异或(图中蓝色圆圈加号),得到新的右半 R i R_i Ri
- 左右更新 :原来的右半 R i − 1 R_{i-1} Ri−1 直接成为新的左半 L i L_i Li
- 输出 : L i L_i Li、 R i R_i Ri
这就是 DES 的一轮完整迭代。16 轮全部跑完之后,把左右两半拼接起来,再经过逆初始置换 IP⁻¹,就得到了最终的 64 位密文。
⚠️ 注意:DES 16轮迭代内部不会自动执行左右交换;迭代完成后,需要手动拼接 R 16 ∥ L 16 R_{16}\|L_{16} R16∥L16,再执行逆初始置换,编写代码时千万不要遗漏。
五、子密钥生成算法
回顾第四章轮函数,DES 每一轮 Feistel 迭代都需要独立的 48 位子密钥 K i K_i Ki。算法不会直接使用原始 64 位主密钥参与运算,而是通过密钥调度算法,迭代生成 K 1 ∼ K 16 K_1 \sim K_{16} K1∼K16 共16组独立子密钥送入每一轮轮函数。
整体流程公式:
64位主密钥 → P C − 1 C 0 ∥ D 0 ( 56 b i t ) → 循环左移 C i ∥ D i → P C − 2 48 b i t 子密钥 K i \text{64位主密钥} \xrightarrow{PC-1} C_0\|D_0\;(56\mathrm{bit}) \xrightarrow{\text{循环左移}} C_i\|D_i \xrightarrow{PC-2} 48\mathrm{bit}\text{子密钥}K_i 64位主密钥PC−1 C0∥D0(56bit)循环左移 Ci∥DiPC−2 48bit子密钥Ki
5.1 PC-1 置换:64 位到 56 位的密钥压缩
DES 输入密钥标称长度64比特,每字节最低比特定义为奇偶校验位,8位校验比特不参与任何加密运算。PC-1置换的两个核心作用:丢弃8个奇偶校验位、剩余56比特重新打乱位置。
PC-1置换表(表格数值 = 选取原始密钥第几位,输出顺序从上至下、从左向右)
| 57 | 49 | 41 | 33 | 25 | 17 | 9 |
|---|---|---|---|---|---|---|
| 1 | 58 | 50 | 42 | 34 | 26 | 18 |
| 10 | 2 | 59 | 51 | 43 | 35 | 27 |
| 19 | 11 | 3 | 60 | 52 | 44 | 36 |
| 63 | 55 | 47 | 39 | 31 | 23 | 15 |
| 7 | 62 | 54 | 46 | 38 | 30 | 22 |
| 14 | 6 | 61 | 53 | 45 | 37 | 29 |
| 21 | 13 | 5 | 28 | 20 | 12 | 4 |
PC-1输出56比特数据,均等切分为两组:
- 前28比特: C 0 C_0 C0
- 后28比特: D 0 D_0 D0
⚠️ 重点区分:PC-1只做选取与置换,无比特扩展、无异或;奇偶校验位在这一步直接永久丢弃。
5.2 循环左移:为什么每轮移位数不同?
对每一轮 i ∈ 1 , 16 i\in1,16 i∈1,16,对上一轮状态 C i − 1 , D i − 1 C_{i-1},D_{i-1} Ci−1,Di−1 分别执行相同位数的循环左移。循环左移含义:左侧移出的比特追加至分组最右端。
各轮移位规则表:
| 轮数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 左移位数 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 1 |
移位后得到新分组 C i , D i C_i,D_i Ci,Di,一方面保留用于下一轮移位,另一方面拼接送入PC-2生成当前轮子密钥。
差异化移位的设计目的:
- 若固定移位位数, C , D C,D C,D 分组会出现周期性循环,造成多轮子密钥高度相似,产生线性依赖;
- 首尾第1、2、9、16轮仅移位1位,打破完整周期,消除子密钥之间简易推导关系;
- 杜绝攻击者利用子密钥的规律性,构造密钥猜测路径。
5.3 PC-2 置换:56 位到 48 位的子密钥选择
将移位完成后的 C i ∥ D i C_i\|D_i Ci∥Di(合计56比特)送入PC-2置换。PC-2会从56比特中挑选48个比特并打乱顺序,丢弃8个比特 ,最终输出48位子密钥 K i K_i Ki,恰好匹配轮函数异或层的数据长度。
PC-2置换表(数值代表选取 C i ∥ D i C_i\|D_i Ci∥Di 的第几位)
| 14 | 17 | 11 | 24 | 1 | 5 |
|---|---|---|---|---|---|
| 3 | 28 | 15 | 6 | 21 | 10 |
| 23 | 19 | 12 | 4 | 26 | 8 |
| 16 | 7 | 27 | 20 | 13 | 2 |
| 41 | 52 | 31 | 37 | 47 | 55 |
| 30 | 40 | 51 | 45 | 33 | 48 |
| 44 | 49 | 39 | 56 | 34 | 53 |
| 46 | 42 | 50 | 36 | 29 | 32 |
关键特性:PC-1、PC-2两张置换相互独立,不存在互逆关系;连续两次置换增加密钥比特扩散程度,增大从子密钥反向还原原始主密钥的计算代价。
5.4 16 轮子密钥完整生成流程
完整执行步骤汇总:
- 输入64比特原始密钥,依照PC-1表筛选比特,丢弃8位奇偶校验位,得到56比特,并拆分为 C 0 、 D 0 C_0、D_0 C0、D0;
- 循环执行 1~16 轮:
- 根据轮数规则,对 C i − 1 C_{i-1} Ci−1、 D i − 1 D_{i-1} Di−1 同步循环左移;
- 移位结果记为 C i 、 D i C_i、D_i Ci、Di;
- 拼接 C i ∥ D i C_i\|D_i Ci∥Di,送入PC-2置换;
- PC-2输出48比特,作为本轮子密钥 K i K_i Ki;
- 循环结束,得到有序子密钥序列 { K 1 , K 2 , ... , K 16 } \{K_1,K_2,\dots,K_{16}\} {K1,K2,...,K16}。
加密与解密子密钥使用规则:
加密顺序: K 1 → K 2 → ⋯ → K 16 K_1 \to K_2 \to \dots \to K_{16} K1→K2→⋯→K16
解密顺序: K 16 → K 15 → ⋯ → K 1 K_{16} \to K_{15} \to \dots \to K_{1} K16→K15→⋯→K1
安全小结子密钥调度属于确定性算法,不存在随机成分。只要原始主密钥固定,16个子密钥完全确定。一旦主密钥被暴力破解,全部子密钥随之泄露,这也是56位短密钥最大短板之一。
六、多重DES:DES 的兼容改进方案
既然标准DES存在56位密钥长度不足的致命短板,无法抵抗现代暴力破解,在全新分组密码标准诞生之前,业界提出了多重DES方案,通过多次迭代加密拓展有效密钥长度,最具代表性的便是3DES(Triple DES)。
6.1 双重 DES(2DES)
设想最简单的改进思路:使用两组独立密钥依次加密:
C = E K 2 ( E K 1 ( P ) ) C = E_{K_2}(E_{K_1}(P)) C=EK2(EK1(P))
直观上大家会认为有效密钥长度提升至 56 × 2 = 112 56\times2=112 56×2=112 位。
但中间相遇攻击(Meet-in-the-Middle) 可以将攻击复杂度压缩至约 2 57 2^{57} 257,安全增益十分有限,无法达到预期安全强度,因此双重DES从未正式投入大规模使用。
6.2 三重 DES(3DES / TDEA)
三重DES执行三次DES运算,存在三种主流密钥模式:
- 3密钥模式(3TDEA,推荐) : C = E K 3 ( D K 2 ( E K 1 ( P ) ) ) C=E_{K_3}(D_{K_2}(E_{K_1}(P))) C=EK3(DK2(EK1(P)))
K 1 , K 2 , K 3 K_1,K_2,K_3 K1,K2,K3 相互独立,等效有效密钥长度112 bit(安全强度上限)。 - 2密钥模式(2TDEA) : K 1 = K 3 ≠ K 2 K_1=K_3 \neq K_2 K1=K3=K2,有效密钥长度约80 bit。
- 单密钥模式 : K 1 = K 2 = K 3 K_1=K_2=K_3 K1=K2=K3,等价于标准DES,无任何安全提升。
加解密规则说明:加密顺序为 加密→解密→加密;解密对应反向:解密→加密→解密。
6.3 3DES 的优劣与现状
优势:完全复用DES组件,能够兼容老旧基于DES的业务系统,是过渡时期的折中方案。
明显短板:执行三次DES运算,运算开销巨大,吞吐量远低于AES。
如今3DES仅存在于遗留金融、老旧工控系统做兼容;新建系统规范中已经禁止选用3DES,优先采用AES或国密SM4。
补充说明
本章仅对多重DES做基础介绍。至于中间相遇攻击完整原理、推导过程,归入后续DES密码分析专题展开讲解。同时DES原生的弱密钥、互补对称性、差分密码分析等安全缺陷,我们同样放在独立专题深入剖析。
七、总结:Feistel 结构与 DES 的历史遗产
本文完整梳理了经典 Feistel 网络范式,并以 DES 作为典型实现案例,剖析了整体架构、轮函数、S盒非线性变换以及密钥调度流程。
作为分组密码两大主流架构之一,Feistel 结构最大创新在于将分组划分为左右两半迭代运算,轮函数无需设计可逆变换,极大降低了密码设计难度,同时实现加解密电路高度对称。在 DES 之后,Blowfish、Twofish、国密 SM4 等算法都沿用广义 Feistel 的设计思想,足以看出该范式深远的影响力。
DES 是全球首个广泛商用标准化分组密码,推动现代对称密码工程体系成型。但受制于时代局限,56 位有效密钥长度使其难以抵御现代暴力破解,因此业界提出多重 DES(2DES、3DES)作为过渡兼容方案。然而多重 DES 只是折中补救手段,性能开销高,无法从根源解决 DES 架构遗留问题。
需要再次区分:Feistel ≠ DES 。Feistel 是通用架构,DES 只是该架构下的一个具体实例。
在下一篇文章,我们将转向分组密码另一核心设计范式:SPN 结构,讲解混淆、扩散、S盒与P盒的基础理论,为后续 AES 的深度解析搭建理论基础。
本文侧重于架构原理与算法流程实现。DES 的弱密钥、互补对称性,以及中间相遇攻击、差分密码分析等各类密码分析手段,内容体量较大,后续将设立独立专题详细推演。
免责声明
本文所有内容仅用于密码学理论学习、高校课程研究、CTF竞赛合法训练,仅供技术原理科普。
文中讲解的各类密码分析思路、攻击方法,禁止直接用于未获得授权的任何系统、设备、业务环境。
未经授权开展密码破解、数据窃听等行为违反《中华人民共和国网络安全法》《中华人民共和国密码法》《中华人民共和国刑法》相关规定,由此产生一切法律责任由行为人自行承担。