1、什么是 DES
DES(Data Encryption Standard)是一种对称加密算法。它是在 20 世纪 70 年代初期由 IBM 研发的。它的设计目标是提供高度的数据安全性和性能,并且能够在各种硬件和软件平台上实现。
2、DES 的基本概念
DES 使用 56 位的密钥和 64 位的明文块进行加密。DES 算法的分组大小是 64 位,因此,如果需要加密的明文长度不足 64 位,需要进行填充;如果明文长度超过 64 位,则需要使用分组模式进行分组加密。
虽然 DES 算法的分组大小是 64 位,但是由于 DES 算法的密钥长度只有 56 位,因此 DES 算法存在着弱点,容易受到暴力破解和差分攻击等攻击手段的威胁。因此,在实际应用中,DES 算法已经不再被广泛使用,而被更加安全的算法所取代,如 AES 算法等。
尽管 DES 已经被取代,但它在密码学的历史上仍然具有重要意义。通过 DES 可以帮助我们了解对称密钥加密算法的基本概念和运作原理。
3、DES 的加密流程
当输入了一条 64 位的数据之后,DES 将通过以下步骤进行加密。
- 初始置换(IP 置换):将输入的 64 位明文块进行置换和重新排列,生成新的 64 位数据块。
- 加密轮次:DES 加密算法共有 16 个轮次,每个轮次都包括四个步骤:
- 将 64 位数据块分为左右两个 32 位块。
- 右侧 32 位块作为输入,经过扩展、异或、置换等操作生成一个 48 位的数据块。这个 48 位的数据块被称为'轮密钥',它是根据加密算法的主密钥生成的子密钥。
- 将左侧 32 位块和轮密钥进行异或运算,结果作为新的右侧 32 位块。
- 将右侧 32 位块与原来的左侧 32 位块进行连接,生成一个新的 64 位数据块,作为下一轮的输入。
- 末置换(FP 置换):在最后一个轮次完成后,将经过加密的数据块进行置换和重新排列,得到加密后的 64 位密文。
总的来说,DES 加密的过程就是通过一系列置换、异或、扩展等运算,将明文分成若干个小块,然后根据主密钥生成一系列的轮密钥,利用轮密钥对每个小块进行加密,最终将加密结果重新组合成一个整体,得到密文。
4、DES 算法步骤详解
4.1 初始置换 (Initial Permutation, IP 置换)
IP 置换是将输入的 64 位明文块进行置换和重新排列,生成新的 64 位数据块。
目的: 增加加密的混乱程度,使明文中的每一位都能够对后面的加密过程产生影响,提高加密强度。
我们将把 64 位的顺序按下表中规定的顺序放置,图中的数字是在 64 位明文中每个比特的索引位置。注意,在 DES 中,这个置放规则是固定的。
即将原来位于第 58 个位置的数据放在第 1 个位置,原来位于第 50 个位置的元素放在第 2 个位置,第 42 个放在第 3 个,34->4 以此类推…
初始置换的逆置换(Final Permutation, FP 置换)是将加密后的数据块进行置换和重新排列,得到最终的加密结果,与初始置换相对应。
4.2 加密轮次
初始置换完成后,明文被划分成了相同长度(32 位)的左右两部分,记作 L0, R0。接下来就会进行 16 个轮次的加密了。
我们从单独一个轮次来看。首先把目光聚焦在 R0 这里。
右半部分 R0 会作为下一轮次的左半部分 L1 的输入。其次,R0 会补位到 48 位和本轮次生成的 48 位 K0(马上讲 K0 的生成) 输入到 F 轮函数中去。F 函数的输出结果为 32 位,结果 F(R0,K0) 会和 L0 进行异或运算作为下一轮次右半部分 R1 的输入。
以此类推,重复 16 轮运算。所以,上面描述的过程可以用以下公式表述。
4.3 F 轮函数
我们讲到在每轮加密中,会将 R 和 K 输入到 F 中,接下来我们看看 F 函数中做了哪些处理。
4.3.1 拓展 R 到 48 位
将 32 位的 R0 右半部分进行扩展,得到一个 48 位的数据块。同样的,数据拓展也是根据一个固定的置换表。红框中就是我们要补位的数据。
由此可见,扩展过程的每一位都是根据上述的置换表从输入的 32 位数据块中提取出来的。原始数据的第 32 位被补充到了新增列的第一个,第 5 位被补充到了第二个新增列的第一个,以此类推…
4.3.2 子密钥 K 的生成
DES 算法采用了每轮子密钥生成的方式来增加密钥的复杂性和安全性。每轮子密钥都是由主密钥(64 位)通过密钥调度算法(Key Schedule Algorithm)生成的。DES 算法的密钥调度算法可以将 64 位的主密钥分成 16 个子密钥,每个子密钥 48 位,用于每轮加密中与输入数据进行异或运算。
通过子密钥生成的流程图来看下整个过程。
- 将 64 位主密钥经过置换选择 1(Permuted Choice 1 简写为 PC-1)后输出了 56 位,将其分为左右两个 28 位的数据块,分别记为 C0 和 D0。同上面我们讲过的置换规则一样,PC-1 置换函数也是一个固定的置换表。
从 PC-1 的置换表中可以看到,舍弃掉的 8 位数据是原始数据中每 8 位数据的最后一位,也就是我们所熟知的奇偶检验位。这 8 位被丢弃是因为它们对于密钥的安全性没有贡献,而且能够使 DES 算法的计算速度更快。
- 对 C0 和 D0 进行循环左移操作。循环左移完成后生成 C1 和 D1。因此,在 16 个轮次的计算当中会得到 16 个 32 位的数据块 C1-C16 和 D1-D16。在 DES 中循环左移也有固定的规则。
对于 i=1,2,…, 16,对于 Ci 和 Di,若 i 为 1,2,9 或 16,则循环左移一位,否则循环左移两位。
- 对于 C1,D1,将它们经过置换选择 2(Permuted Choice 2 简写位 PC-2)后,得到 48 位的子密钥 K1,用于每轮加密中与输入数据进行异或运算。PC-2 置换的输入是由 PC-1 置换生成的 56 位的密钥,而它的输出是 48 位的子密钥。PC-2 置换将 56 位的密钥重新排列,丢弃了 8 位并选取了其中的 48 位作为子密钥。PC-2 的置换规则如下:
| 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-2 置换表的第一行表示选择了输入密钥中的第 14、17、11、24、1 和 5 位,并将它们作为输出子密钥的前 6 位。以此类推…
- 至此,经过 PC-2 后的结果就是我们当前轮次的子密钥 K1 了。在整个 DES 加密过程中会生成 16 个 48 位子密钥 K1-K16,分别用于 DES 算法中的 16 轮加密过程,从而保证每轮加密所使用的密钥都是不同的,增加了破解的难度。
4.3.3 当前轮次的子密钥与拓展的 48 位 R 进行异或运算
当前轮次的子密钥 Ki 与拓展的 48 位 Ri 进行异或运算。运算结果会作为接下来 S 盒替换的输入。
4.3.4 S 盒替换(Substitution Box substitution)
S 盒替换(Substitution Box substitution)是一种在密码学中广泛使用的加密技术。它是将明文中的一组比特映射到密文中的一组比特的过程,用于增强密码的安全性。DES 中 S 盒替换用于将上一轮异或运算的 48 位结果映射到 32 位输出中去。
同样的,S 盒也是一种置换表。在 DES 的每一轮计算中 S 盒都是不一样的。这里我以第一轮计算中的 S 盒为例。从上图中我们看到,S 盒内部有 8 个 S 块,记作 S1-S8。每个 S 块都会接收 6 位字符作为输入并输出四位字符。这里我们以第一个 S 盒 S1 为例。他是一个 4*16 的置换表。
| 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 |
例如输入 101010 到 S1 中。S1 会将这六位的第一位和第六位拿出来 10 作为 S1 的行,中间四位 0101 拿出来作为 S1 的列。我们转换成十进制,此时映射到这个 S 盒的位置就是 (2,5) 对应 S 盒的第 3 行第 6 列(索引都从 0 开始数)。
所以这个输入的结果是 6,将 6 转化为二进制 110,S 盒的输出是 4 位,所以得 S(101010)=0110
因此,可以看到 S 盒其实是一种非线性的加密技术,它能够抵御许多传统的密码分析攻击,如差分攻击和线性攻击。
4.3.5 P 盒替换
P 盒替换将 S 盒替换的 32 位输出作为输入,经过上述固定的替换表进行替换后即为最后 F 轮函数的结果。
该结果 F(R0,K0) 与 L0 进行异或运算得到下一轮的右半部分 R1
4.4 逆置换(Inverse Permutation)
在经过 16 轮次计算后,DES 会对最后的结果进行最后一次置换。即为最后的输出结果。
5、DES 的优缺点
优点:
- 安全性高:DES 算法使用密钥进行加密和解密,相同的明文使用不同的密钥加密后得到的密文是不同的。密钥越长,加密的安全性就越高。
- 算法简单:DES 算法的加密和解密过程非简单,基于对称加密,使用相同的 key 进行加解密。
- 适用广泛:DES 算法是最早也是最广泛使用的加密算法之一,被广泛应用于电子商务、电子邮件、虚拟私人网络等领域,具有广泛的适用性和可移植性。
缺点:
- 密钥长度较短:DES 算法使用 56 位密钥,虽然在当时足够安全,但在当前计算机的处理能力下,已经不足以保证加密的安全性,易受到暴力破解攻击。
- 无法抵抗差分密码分析攻击:DES 算法无法抵抗差分密码分析攻击,这种攻击可以通过比较相同明文的密文,分析加密算法的行为并推断出密钥。
- 比较慢:由于 DES 算法是一种分组密码算法,需要对 64 位的明文进行加密,加密速度比较慢,不适用于对大量数据进行实时加密和解密。
6、DES 的攻击方法
由于 DES 从诞生距今已经很多年了,但是仍然有部分老旧的系统会使用 DES 进行加密。因为其密钥长度较短(仅 56 位)和已知的弱点,因此容易受到以下攻击。
穷举攻击(Brute-Force Attack):由于 DES 算法的密钥长度较短,可能受到暴力破解攻击,攻击者可以通过穷举所有可能的密钥来尝试破解密文。尽管 DES 算法的加密速度比较慢,但现代计算机的计算能力很强,可以在合理时间内进行暴力破解攻击。
差分密码分析攻击(Differential Cryptanalysis Attack):差分密码分析是一种比较高效的攻击方式,可以通过对明文和密文之间的差异进行分析,推导出密钥。对于 DES 算法,攻击者可以通过分析不同的输入和输出差异,以及密钥可能取值的概率,从而获得密钥。
线性密码分析攻击(Linear Cryptanalysis Attack):线性密码分析是一种比较有效的攻击方式,可以通过线性近似计算找到密钥。对于 DES 算法,攻击者可以通过构造一些线性逼近,以及计算相应的概率,从而推导出密钥。
工作密钥攻击(Known Plaintext Attack):在工作密钥攻击中,攻击者可以获得一些已知明文和相应的密文,然后利用这些信息来推导出密钥。对于 DES 算法,攻击者可以通过获得足够的已知明文和密文,来推导出密钥。
生日攻击(Birthday Attack):生日攻击是一种利用概率学的攻击方式,可以在相对较短的时间内找到具有相同散列值的两个不同的输入。对于 DES 算法,攻击者可以使用生日攻击来找到两个不同的密钥,这些密钥都可以加密相同的明文。
7、3DES(Triple DES)
我们讲到了 DES 目前而言是不安全的。因此也诞生了 3DES 这样的算法来对 DES 进行加强。3DES 顾名思义,就是使用 DES 加密 3 次,使用 3 个密钥进行加解密。
为什么是 3DES 不是 2DES
3DES 使用了三个密钥,将 DES 算法的加密过程重复三次,从而大大增强了安全性。3DES 的密钥长度为 168 位,远高于 DES 算法的 56 位密钥长度和 2DES 算法的 112 位密钥长度。2DES 暴力破解的时间复杂度为 O(2^57),仍然有很大的可能被暴力破解。
举个例子看下:
对于明文 P, 密文 C, C 由经过两次加密 Ek1,Ek2 得到 K1,K2 分别是第一二次加密的密钥。
- 遍历 P 到第一次加密的 K1,最多有 2^56 种可能。
- 由于加解密是相同的 key。所以我们同时还能对 C 进行暴力解密找 key。同样有最多 2^56 种可能。
- 当我们遍历能找到一个 Ek1(P)=Dk2(C) 使用这个密钥去加解密不同的明文和加密结果 (P',C')。
- 如果能成功,那么此次暴力破解的时间复杂度为 O(2^57)。因此依旧不够安全。
8、Python 实现 DES
在 Python 中,我们可以使用 pycryptodome 库来实现 DES 加密和解密。以下是完整的示例代码。
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
import base64
# 填充函数
pad = lambda s: s + (8 - len(s) % 8) * chr(8 - len(s) % 8).encode()
unpad = lambda s: s[:-s[-1]]
# 生成随机密钥 (必须是 8 字节)
def generate_key():
return get_random_bytes(8)
# 加密函数
def encrypt(key, plaintext):
cipher = DES.new(key, DES.MODE_ECB)
padded_text = pad(plaintext.encode())
ciphertext = cipher.encrypt(padded_text)
return base64.b64encode(ciphertext).decode()
# 解密函数
def decrypt(key, ciphertext):
cipher = DES.new(key, DES.MODE_ECB)
decoded_ciphertext = base64.b64decode(ciphertext)
decrypted_padded = cipher.decrypt(decoded_ciphertext)
return unpad(decrypted_padded).decode()
if __name__ == "__main__":
key = generate_key()
message = "Hello, DES Encryption!"
print(f"Original Message: {message}")
print(f"Key: {key.hex()}")
encrypted = encrypt(key, message)
print(f"Encrypted: {encrypted}")
decrypted = decrypt(key, encrypted)
print(f"Decrypted: {decrypted}")
运行上述代码,您将看到明文被加密为 Base64 编码的密文,随后又被还原为原始明文。请注意,ECB 模式存在安全隐患,实际生产环境中建议使用 CBC 或 GCM 模式,并配合 IV(初始化向量)。


