概述
数据加密标准(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实现(完整注释版)
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. 初始置换IP
boolean[] 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);
}
}
- 演示
java
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实现
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替代
-
选择安全模式:
java// 推荐使用CBC或CTR模式 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-
密钥管理最佳实践:
java// 使用安全随机数生成密钥 KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); // 256位密钥 SecretKey secretKey = keyGen.generateKey();
-
完整性保护:
java// 结合HMAC或使用认证加密模式 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
历史意义与总结
DES作为密码学发展史上的里程碑:
- 开创了公开密码算法的标准化时代
- 推动了分组密码设计理论研究
- 促进了密码分析技术的进步
- 为现代加密标准(如AES)奠定了基础
尽管DES已完成历史使命,其精巧的Feistel结构设计、S盒构造方法和密钥调度算法,仍为密码学研究者提供了宝贵的学习范例。理解DES的工作原理,是掌握现代密码学不可或缺的基础。