概述
数据加密标准(Data Encryption Standard, DES)是1977年由美国国家标准局(NIST)采纳的对称密钥加密算法,作为首个公开的联邦信息处理标准(FIPS PUB 46)。DES采用64位分组大小和56位有效密钥长度(外加8位奇偶校验位),通过16轮Feistel网络结构实现数据加密。尽管已被AES取代,DES仍是理解现代密码学的基石。
历史意义
- 首个公开的加密标准(1977-2005)
- 推动了密码学研究的发展
- 启发了3DES、AES等后续算法
- 证明了Feistel网络的实际可行性
核心结构与特点
1. Feistel网络结构
1. 64位明文输入
2. 初始置换(IP)
3. 分割为L0(左32位)和R0(右32位)
4. 16轮迭代处理:- 每轮:R_i = L_{i-1} ⊕ F(R_{i-1}, K_i)- L_i = R_{i-1}
5. 合并R16和L16
6. 逆初始置换(IP⁻¹)
7. 输出64位密文
2. 密钥特性
特性 | 说明 |
---|---|
有效长度 | 56位(64位含8位奇偶校验) |
密钥调度 | 16轮子密钥生成算法 |
密钥空间 | 2⁵⁶ ≈ 7.2×10¹⁶种可能 |
密钥输入 | 64位(含8位校验位) |
子密钥 | 16个48位轮密钥 |
3. 安全设计要素
- 混淆:通过8个S盒实现非线性变换
- 扩散:通过P盒置换实现比特扩散
- 雪崩效应:1比特输入变化导致约50%输出比特改变
- 抗差分分析:S盒精心设计抵抗差分攻击
- 轮结构:16轮迭代确保充分混淆扩散
DES加解密详细过程
加密流程
1. 64位明文输入
2. 初始置换IP
3. 分割为L0(左32位)和R0(右32位)
4. 16轮迭代:第1轮:R1 = L0 ⊕ F(R0, K1), L1 = R0第2轮:R2 = L1 ⊕ F(R1, K2), L2 = R1...第16轮:R16 = L15 ⊕ F(R15, K16), L16 = R15
5. 合并R16和L16(注意:不交换最后结果)
6. 逆初始置换IP⁻¹
7. 输出64位密文
轮函数F核心处理
1. 32位输入
2. 扩展置换E:32位→48位
3. 与48位轮密钥异或
4. S盒替换:48位→32位(8个S盒并行处理)
5. P盒置换:32位→32位
6. 32位输出
密钥调度算法
1. 64位密钥输入
2. 置换选择PC-1:64位→56位(移除校验位)
3. 分割为C0(左28位)和D0(右28位)
4. 16轮处理:- 循环左移(1或2位,根据轮次)- 置换选择PC-2:56位→48位(生成轮密钥Ki)
5. 输出16个48位轮密钥(K1-K16)
Java实现(完整注释版)
import java.security.SecureRandom;
import java.util.Arrays;public class DESAlgorithm {private static final int BLOCK_SIZE = 8; // DES分组大小// 初始置换表 (IP)private static final int[] INITIAL_PERMUTATION = {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⁻¹)private static final int[] INVERSE_PERMUTATION = {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};// 扩展置换表 (E)private static final int[] EXPANSION_TABLE = {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};// DES标准的8个S盒定义(4行×16列)private static final int[][][] 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}}};// P盒置换表private static final int[] P_BOX_PERMUTATION = {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};// 密钥置换表 (PC-1)private static final int[] 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};// 轮移位表private static final int[] SHIFT_SCHEDULE = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};// 密钥压缩置换 (PC-2)private static final int[] 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};/*** 执行位置换操作* @param input 输入位数组* @param table 置换表* @return 置换后的位数组*/private static boolean[] permute(boolean[] input, int[] table) {boolean[] output = new boolean[table.length];for (int i = 0; i < table.length; i++) {output[i] = input[table[i] - 1];//注意减去1,因为数组从0开始}return output;}/*** 执行S盒替换 (DES核心非线性变换)* @param input 48位输入* @return 32位输出*/private static boolean[] sBoxSubstitution(boolean[] input) {if (input.length != 48)throw new IllegalArgumentException("S盒输入必须为48位");boolean[] output = new boolean[32];for (int i = 0; i < 8; i++) {// 提取6位输入boolean[] bs = Arrays.copyOfRange(input, i*6, (i+1)*6);// 计算行号 (第1和6位)int row = (bs[0] ? 2 : 0) | (bs[5] ? 1 : 0);//b1和b6位组合// 计算列号 (中间4位)int col = 0;for (int j = 1; j <= 4; j++) {if (bs[j]) col |= (1 << (4 - j));//中间4位组合}// 从S盒获取4位输出int sBoxValue = S_BOXES[i][row][col];// 转换为4位布尔数组int outputPos = 0;for (int j = 0; j < 4; j++) {output[outputPos++] = ((sBoxValue >> (3 - j)) & 1) == 1;}}return output;}/*** 轮函数F* @param right 32位右半部分* @param roundKey 48位轮密钥* @return 32位输出*/private static boolean[] fFunction(boolean[] right, boolean[] roundKey) {// 1. 扩展置换 (32→48位)boolean[] expanded = permute(right, EXPANSION_TABLE);// 2. 与轮密钥异或boolean[] xored = new boolean[48];for (int i = 0; i < 48; i++) {xored[i] = expanded[i] ^ roundKey[i];}// 3. S盒替换 (48→32位)boolean[] substituted = sBoxSubstitution(xored);// 4. P盒置换return permute(substituted, P_BOX_PERMUTATION);}/*** 密钥调度算法 (生成16轮子密钥)* @param key 64位密钥 (含8位奇偶校验)* @return 16个48位轮密钥*/private static boolean[][] generateSubKeys(boolean[] key) {// 1. PC-1置换 (64→56位)boolean[] pc1 = permute(key, PC1);// 2. 分为左右28位boolean[] c0 = Arrays.copyOfRange(pc1, 0, 28);boolean[] d0 = Arrays.copyOfRange(pc1, 28, 56);boolean[][] subKeys = new boolean[16][48];for (int round = 0; round < 16; round++) {// 3. 循环左移int shift = SHIFT_SCHEDULE[round];c0 = rotateLeft(c0, shift);d0 = rotateLeft(d0, shift);// 4. 合并为56位boolean[] cd = new boolean[56];System.arraycopy(c0, 0, cd, 0, 28);System.arraycopy(d0, 0, cd, 28, 28);// 5. PC-2置换 (56→48位)subKeys[round] = permute(cd, PC2);}return subKeys;}/*** 循环左移* @param bits 输入位数组* @param shift 移位位数* @return 移位后位数组*/private static boolean[] rotateLeft(boolean[] bits, int shift) {boolean[] result = new boolean[bits.length];for (int i = 0; i < bits.length; i++) {result[i] = bits[(i + shift) % bits.length];}return result;}/*** DES加密核心* @param block 64位明文分组* @param key 64位密钥* @return 64位密文分组*/public static boolean[] desEncryptBlock(boolean[] block, boolean[] key) {// 1. 生成16轮子密钥boolean[][] subKeys = generateSubKeys(key);// 2. 初始置换IPboolean[] ip = permute(block, INITIAL_PERMUTATION);// 3. 分为左右32位boolean[] left = Arrays.copyOfRange(ip, 0, 32);boolean[] right = Arrays.copyOfRange(ip, 32, 64);// 4. 16轮Feistel迭代for (int round = 0; round < 16; round++) {boolean[] temp = right;boolean[] fResult = fFunction(right, subKeys[round]);// 左半部分与F函数结果异或boolean[] newRight = new boolean[32];for (int i = 0; i < 32; i++) {newRight[i] = left[i] ^ fResult[i];}// 更新左右部分left = temp;right = newRight;}// 5. 最终交换 (R16 L16)boolean[] rl = new boolean[64];System.arraycopy(right, 0, rl, 0, 32);System.arraycopy(left, 0, rl, 32, 32);// 6. 逆初始置换IP⁻¹return permute(rl, INVERSE_PERMUTATION);}/*** DES解密核心* @param block 64位密文分组* @param key 64位密钥* @return 64位明文分组*/public static boolean[] desDecryptBlock(boolean[] block, boolean[] key) {// 生成子密钥boolean[][] subKeys = generateSubKeys(key);// 解密时逆序使用子密钥boolean[][] reverseKeys = new boolean[16][48];for (int i = 0; i < 16; i++) {reverseKeys[i] = subKeys[15 - i];}// 其余步骤与加密相同boolean[] ip = permute(block, INITIAL_PERMUTATION);boolean[] left = Arrays.copyOfRange(ip, 0, 32);boolean[] right = Arrays.copyOfRange(ip, 32, 64);for (int round = 0; round < 16; round++) {boolean[] temp = right;boolean[] fResult = fFunction(right, reverseKeys[round]);boolean[] newRight = new boolean[32];for (int i = 0; i < 32; i++) {newRight[i] = left[i] ^ fResult[i];}left = temp;right = newRight;}boolean[] rl = new boolean[64];System.arraycopy(right, 0, rl, 0, 32);System.arraycopy(left, 0, rl, 32, 32);return permute(rl, INVERSE_PERMUTATION);}/*** 字节数组转位数组* @param bytes 字节数组* @return 位数组*/public static boolean[] bytesToBits(byte[] bytes) {boolean[] bits = new boolean[bytes.length * 8];for (int i = 0; i < bytes.length; i++) {for (int j = 0; j < 8; j++) {bits[i * 8 + j] = ((bytes[i] >> (7 - j)) & 1) == 1;}}return bits;}/*** 位数组转字节数组* @param bits 位数组* @return 字节数组*/public static byte[] bitsToBytes(boolean[] bits) {byte[] bytes = new byte[bits.length / 8];for (int i = 0; i < bytes.length; i++) {for (int j = 0; j < 8; j++) {if (bits[i * 8 + j]) {bytes[i] |= (1 << (7 - j));}}}return bytes;}public static byte[] generateRandomKey() {SecureRandom random = new SecureRandom();byte[] key = new byte[8]; // 64位密钥random.nextBytes(key);// 设置奇偶校验位(DES标准要求)for (int i = 0; i < 8; i++) {int parity = Integer.bitCount(key[i] & 0xFF) % 2;key[i] = (byte) ((key[i] & 0xFE) | (parity == 0 ? 1 : 0));}return key;}/*** PKCS#7填充* @param data 需要填充的数据* @param blockSize 块大小(DES为8字节)* @return 填充后的数据*/public static byte[] pkcs7Pad(byte[] data, int blockSize) {int paddingLength = blockSize - (data.length % blockSize);byte[] padded = new byte[data.length + paddingLength];System.arraycopy(data, 0, padded, 0, data.length);// 填充字节的值等于填充长度Arrays.fill(padded, data.length, padded.length, (byte) paddingLength);return padded;}/*** PKCS#7去除填充* @param data 带填充的数据* @return 去除填充后的数据* @throws IllegalArgumentException 如果填充无效*/public static byte[] pkcs7Unpad(byte[] data) {// 检查填充长度是否有效int paddingLength = data[data.length - 1] & 0xFF;if (paddingLength < 1 || paddingLength > data.length) {throw new IllegalArgumentException("无效的PKCS#7填充");}// 验证所有填充字节是否正确for (int i = data.length - paddingLength; i < data.length; i++) {if (data[i] != paddingLength) {throw new IllegalArgumentException("无效的PKCS#7填充");}}return Arrays.copyOfRange(data, 0, data.length - paddingLength);}/*** DES加密长文本* @param plaintext 明文* @param key 64位密钥(8字节)* @return 密文*/public static byte[] encrypt(byte[] plaintext, byte[] key) {// 1. 应用PKCS#7填充byte[] padded = pkcs7Pad(plaintext, 8);// 2. 将密钥转换为位数组boolean[] keyBits = bytesToBits(key);// 3. 分组加密byte[] ciphertext = new byte[padded.length];for (int i = 0; i < padded.length; i += 8) {// 提取当前分组byte[] block = Arrays.copyOfRange(padded, i, i + 8);boolean[] blockBits = bytesToBits(block);// 加密分组boolean[] encryptedBits = desEncryptBlock(blockBits, keyBits);// 转换回字节并存储byte[] encryptedBlock = bitsToBytes(encryptedBits);System.arraycopy(encryptedBlock, 0, ciphertext, i, 8);}return ciphertext;}/*** DES解密长文本* @param ciphertext 密文* @param key 64位密钥(8字节)* @return 明文*/public static byte[] decrypt(byte[] ciphertext, byte[] key) {// 1. 检查输入长度if (ciphertext.length % 8 != 0) {throw new IllegalArgumentException("密文长度必须是8的倍数");}// 2. 将密钥转换为位数组boolean[] keyBits = bytesToBits(key);// 3. 分组解密byte[] decryptedWithPadding = new byte[ciphertext.length];for (int i = 0; i < ciphertext.length; i += 8) {// 提取当前分组byte[] block = Arrays.copyOfRange(ciphertext, i, i + 8);boolean[] blockBits = bytesToBits(block);// 解密分组boolean[] decryptedBits = desDecryptBlock(blockBits, keyBits);// 转换回字节并存储byte[] decryptedBlock = bitsToBytes(decryptedBits);System.arraycopy(decryptedBlock, 0, decryptedWithPadding, i, 8);}// 4. 去除填充return pkcs7Unpad(decryptedWithPadding);}}
- 演示
public class Main {public static void main(String[] args) {byte[] key=DESAlgorithm.generateRandomKey();//生成随机密钥String str="尽管DES已完成历史使命,其精巧的Feistel结构设计、S盒构造方法和密钥调度算法,仍为密码学研究者提供了宝贵的学习范例。理解DES的工作原理,是掌握现代密码学不可或缺的基础。";byte[] msg= str.getBytes();byte[] c = DESAlgorithm.encrypt(msg, key);//加密String encryptedText = new String(c);byte[] m = DESAlgorithm.decrypt(c,key);//解密String decryptedText = new String(m);System.out.println(encryptedText);System.out.println(decryptedText);}
}
DES安全分析与演进
安全漏洞
- 密钥长度不足:56位密钥易受暴力破解
- 弱密钥问题:某些密钥导致加密强度降低
- 半弱密钥:某些密钥对产生可逆加密
- 线性与差分分析:理论攻击方法复杂度为2⁴³
增强方案:3DES
加密:C = E(K3, D(K2, E(K1, P)))
解密:P = D(K1, E(K2, D(K3, C)))
3DES Java实现
public static byte[] tripleDESEncrypt(byte[] plaintext, byte[] key) throws Exception {if (key.length != 24) throw new IllegalArgumentException("3DES需要24字节密钥");byte[] key1 = Arrays.copyOfRange(key, 0, 8);byte[] key2 = Arrays.copyOfRange(key, 8, 16);byte[] key3 = Arrays.copyOfRange(key, 16, 24);// 加密:E(K3, D(K2, E(K1, P)))byte[] step1 = encryptWithJCE(plaintext, key1);byte[] step2 = decryptWithJCE(step1, key2);return encryptWithJCE(step2, key3);
}
DES与AES对比
特性 | DES | AES |
---|---|---|
密钥长度 | 56位 | 128/192/256位 |
分组大小 | 64位 | 128位 |
结构 | Feistel网络 | SP网络 |
轮数 | 16 | 10/12/14 |
安全性 | 已破解 | 当前安全 |
性能 | 较慢 | 更快 |
应用 | 遗留系统 | 现代标准 |
实际应用建议
-
避免使用纯DES:使用3DES或AES替代
-
选择安全模式:
// 推荐使用CBC或CTR模式 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-
密钥管理最佳实践:
// 使用安全随机数生成密钥 KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); // 256位密钥 SecretKey secretKey = keyGen.generateKey();
-
完整性保护:
// 结合HMAC或使用认证加密模式 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
历史意义与总结
DES作为密码学发展史上的里程碑:
- 开创了公开密码算法的标准化时代
- 推动了分组密码设计理论研究
- 促进了密码分析技术的进步
- 为现代加密标准(如AES)奠定了基础
尽管DES已完成历史使命,其精巧的Feistel结构设计、S盒构造方法和密钥调度算法,仍为密码学研究者提供了宝贵的学习范例。理解DES的工作原理,是掌握现代密码学不可或缺的基础。