DES 加密算法:核心组件、加解密流程与安全特性
DES对称加密算法
一、DES算法原理
算法流程:
初始置换(IP置换):将输入的64位明文块进行置换和重新排列,生成新的64位数据块。
加密轮次:DES加密算法共有16个轮次,每个轮次都包括四个步骤:
将64位数据块分为左右两个32位块。
右侧32位块作为输入,经过扩展、异或、置换等操作生成一个48位的数据块。这个48位的数据块被称为“轮密钥”,它是根据加密算法的主密钥生成的子密钥。
将左侧32位块和轮密钥进行异或运算,结果作为新的右侧32位块。
将右侧32位块与原来的左侧32位块进行连接,生成一个新的64位数据块,作为下一轮的输入。
末置换(FP置换):在最后一个轮次完成后,将经过加密的数据块进行置换和重新排列,得到加密后的64位密文。
算法原理:
PC1(密钥置换选择 1) :将 64 位原始密钥去除 8 位校验位,得到 56 位有效密钥,并调整位顺序。
PC2(密钥置换选择 2) :将 56 位密钥进一步置换为 48 位,生成每轮的子密钥。
IP(初始置换) :对 64 位明文分组进行初始位重排,打破明文原始位顺序。
IP_INV(逆初始置换) :与 IP 互为逆作,用于加密最后一步,还原位顺序。
E(扩展置换) :将 32 位数据扩展为 48 位,使数据长度与子密钥长度匹配
P(P 盒置换) :对 S 盒输出的 32 位数据进行位重排
S_BOXES(S 盒) :8 个 4×16 的非线性替代盒,将 6 位输入转换为 4 位输出
对称性数学验证(以 第 2 轮为例)
以 2 轮 Feistel 迭代为例,可直观验证解密的正确性:
加密过程:(加密输出)。
解密过程(输入,子密钥顺序):第 1 轮解密(用):,(异或抵消轮函数)。第 2 轮解密(用):,(最终还原)。
通过数学推导可见,反向使用子密钥可完全还原原始数据,证明了 DES 加解密的对称性。
二、DES 算法的弱密钥与安全性
弱密钥(Weak Keys)
DES 存在 4 个弱密钥,其特点是 “加密与解密等价”(),且 “加密两次还原明文”(),核心原因是弱密钥生成的 16 轮子密钥完全相同。4 个弱密钥(64 位,十六进制):
0000000000000000
FFFFFFFFFFFFFFFF
00000000FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF00000000
半弱密钥(Semi-weak Keys)
半弱密钥成对存在(共 6 对),特点是 “用 K1 加密后再用 K2 加密,还原明文”(),核心原因是两对密钥生成的子密钥互为补集。经典半弱密钥对(十六进制):
0101010101010101
与 FEFEFEFEFEFEFEFE
1F1F1F1F0E0E0E0E
与 E0E0E0E0F1F1F1F1
01FE01FE01FE01FE
与 FE01FE01FE01FE01
1FE01FE00EF10EF1
与 E01FE01FF10EF10E
01E001E001F101F1
与 E001E001F101F101
1FFE1FFE0EFE0EFE
与 FE1FFE1FFE0EFE0E
三、DES算法代码实现
"""
请使用编程语言实现对称加密算法DES(语言不限)
要求:
论述DES算法的原理及实现过程
请给出DES算法弱密钥,并通过程序验证弱密钥的危害
编程语言不限
"""
import sys
from typing import List, Tuple# DES常量定义
# 密钥置换选择1矩阵
PC1 = [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
]
# 密钥置换选择2矩阵
PC2 = [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
]
# 加密扩展置换矩阵
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
]
# 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
]
# S盒置换矩阵
S_BOXES = [# 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[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],# S3[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],# S4[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],# S5[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],# S6[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],# S7[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],# S8[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]
]
# 初始置换矩阵
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
]
# 初始置换逆矩阵
IP_INV = [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
]# 循环左移位数
SHIFT_SCHEDULE = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]# 根据置换表对数据块进行置换
def permute(block: int, table: List[int], n: int) -> int:"""根据置换表对数据块进行置换"""result = 0for i, pos in enumerate(table):bit = (block >> (n - pos)) & 1result |= (bit << (len(table) - 1 - i))return resultdef generate_subkeys(key: int) -> List[int]:"""从64位密钥生成16个48位子密钥"""# 密钥置换1 (PC1),丢弃8个奇偶校验位key_56 = permute(key, PC1, 64)# 分为C0和D0C = (key_56 >> 28) & 0x0FFFFFFFD = key_56 & 0x0FFFFFFFsubkeys = []for i in range(16):# 循环左移shift = SHIFT_SCHEDULE[i]C = ((C << shift) | (C >> (28 - shift))) & 0x0FFFFFFFD = ((D << shift) | (D >> (28 - shift))) & 0x0FFFFFFF# 合并并应用PC2置换CD = (C << 28) | Dsubkey = permute(CD, PC2, 56)subkeys.append(subkey)return subkeysdef feistel(R: int, subkey: int) -> int:"""Feistel函数"""# 扩展置换expanded = permute(R, E, 32)# 与子密钥异或xored = expanded ^ subkey# S盒替代s_output = 0for i in range(8):block = (xored >> (42 - i * 6)) & 0x3F# row = ((block >> 5) << 1) | (block & 0x01)# col = (block >> 1) & 0x0Frow = ((block & 0x20) >> 4) | (block & 0x01)col = (block >> 1) & 0x0Fval = S_BOXES[i][row][col]s_output |= (val << (28 - i * 4))# P盒置换return permute(s_output, P, 32)def des_block(block: int, subkeys: List[int], encrypt: bool = True) -> int:"""加密或解密单个64位数据块"""# 初始置换block = permute(block, IP, 64)# 分为左右两部分L = (block >> 32) & 0xFFFFFFFFR = block & 0xFFFFFFFF# 16轮Feistel网络for i in range(16):round_key = subkeys[i] if encrypt else subkeys[15 - i]new_L = Rnew_R = L ^ feistel(R, round_key)L, R = new_L, new_R# 合并左右部分combined = (R << 32) | L# 逆初始置换return permute(combined, IP_INV, 64)def pkcs7_pad(data: bytes, block_size: int = 8) -> bytes:"""PKCS#7填充"""pad_len = block_size - (len(data) % block_size)return data + bytes([pad_len] * pad_len)def pkcs7_unpad(data: bytes) -> bytes:"""去除PKCS#7填充"""pad_len = data[-1]if pad_len < 1 or pad_len > 8:raise ValueError("Invalid padding")for b in data[-pad_len:]:if b != pad_len:raise ValueError("Invalid padding")return data[:-pad_len]# 将8字节转换为64位整数
def bytes_to_block(b: bytes) -> int:"""将8字节转换为64位整数"""return int.from_bytes(b, byteorder='big')def block_to_bytes(block: int) -> bytes:"""将64位整数转换为8字节"""return block.to_bytes(8, byteorder='big')def des_encrypt(key: bytes, plaintext: bytes) -> bytes:"""DES加密函数"""if len(key) != 8:raise ValueError("Key must be 8 bytes (64 bits)")key_int = bytes_to_block(key)subkeys = generate_subkeys(key_int)# 填充明文padded = pkcs7_pad(plaintext)ciphertext = []for i in range(0, len(padded), 8):block = bytes_to_block(padded[i:i + 8])encrypted = des_block(block, subkeys, encrypt=True)ciphertext.append(block_to_bytes(encrypted))return b''.join(ciphertext)def des_decrypt(key: bytes, ciphertext: bytes) -> bytes:"""DES解密函数"""if len(key) != 8:raise ValueError("Key must be 8 bytes (64 bits)")if len(ciphertext) % 8 != 0:raise ValueError("Ciphertext length must be multiple of 8")key_int = bytes_to_block(key)subkeys = generate_subkeys(key_int)plaintext = []for i in range(0, len(ciphertext), 8):block = bytes_to_block(ciphertext[i:i + 8])decrypted = des_block(block, subkeys, encrypt=False)plaintext.append(block_to_bytes(decrypted))# 去除填充return pkcs7_unpad(b''.join(plaintext))def test_des():"""测试DES加密解密功能"""key = b'22922922'plaintext = b'This is a test message for DES encryption.'ciphertext = des_encrypt(key, plaintext)decrypted = des_decrypt(key, ciphertext)print("明文:", plaintext)print("密钥:", key)print("密文:", ciphertext.hex())print("解密:", decrypted)assert decrypted == plaintext, "Encryption/decryption failed"def test_weak_keys():"""测试DES弱密钥和半弱密钥"""# 弱密钥(Weak Keys)weak_keys = [b'\x01\x01\x01\x01\x01\x01\x01\x01', b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE', b'\xE0\xE0\xE0\xE0\xF1\xF1\xF1\xF1', b'\x1F\x1F\x1F\x1F\x0E\x0E\x0E\x0E' ]# 半弱密钥对(Semi-weak Key Pairs)semiweak_pairs = [(b'\x01\xFE\x01\xFE\x01\xFE\x01\xFE', b'\xFE\x01\xFE\x01\xFE\x01\xFE\x01'),(b'\x1F\xE0\x1F\xE0\x0E\xF1\x0E\xF1', b'\xE0\x1F\xE0\x1F\xF1\x0E\xF1\x0E'),(b'\x01\xE0\x01\xE0\x01\xF1\x01\xF1', b'\xE0\x01\xE0\x01\xF1\x01\xF1\x01'),(b'\x1F\xFE\x1F\xFE\x0E\xFE\x0E\xFE', b'\xFE\x1F\xFE\x1F\xFE\x0E\xFE\x0E'),(b'\x01\x1F\x01\x1F\x01\x0E\x01\x0E', b'\x1F\x01\x1F\x01\x0E\x01\x0E\x01'),(b'\xE0\xFE\xE0\xFE\xF1\xFE\xF1\xFE', b'\xFE\xE0\xFE\xE0\xFE\xF1\xFE\xF1')]# 使用正好8字节的明文plaintext_block = b'12345678'print(f"\n弱密钥测试明文块: {plaintext_block.decode()}")# ==== 测试弱密钥 ====print("===== 弱密钥测试 =====")for i, key in enumerate(weak_keys):key_int = bytes_to_block(key)subkeys = generate_subkeys(key_int)block = bytes_to_block(plaintext_block)encrypted = des_block(block, subkeys, encrypt=True)double_encrypted = des_block(encrypted, subkeys, encrypt=True)double_encrypted_bytes = block_to_bytes(double_encrypted)print(f"弱密钥 #{i+1}: {key.hex()}")if double_encrypted_bytes == plaintext_block:print("✅ 弱密钥特性成立: 加密两次等于明文")else:print("❌ 弱密钥特性不成立")# 验证解密decrypted = des_block(encrypted, subkeys, encrypt=False)if block_to_bytes(decrypted) == plaintext_block:print("✅ 解密正确")else:print("❌ 解密失败")print("\n===== 半弱密钥测试 =====")for i, (key1, key2) in enumerate(semiweak_pairs):key1_int = bytes_to_block(key1)key2_int = bytes_to_block(key2)subkeys1 = generate_subkeys(key1_int)subkeys2 = generate_subkeys(key2_int)block = bytes_to_block(plaintext_block)# 半弱密钥特性测试:E_{K2}(E_{K1}(P)) = Pencrypted1 = des_block(block, subkeys1, encrypt=True) # K1加密encrypted2 = des_block(encrypted1, subkeys2, encrypt=True) # K2加密print(f"半弱密钥对 #{i + 1}:\n K1: {key1.hex()}\n K2: {key2.hex()}")if block_to_bytes(encrypted2) == block_to_bytes(block):print("✅ 半弱密钥特性成立: E_{K2}(E_{K1}(P)) = P")else:print("❌ 半弱密钥特性不成立: E_{K2}(E_{K1}(P)) != P")if __name__ == "__main__":test_des()test_weak_keys()