Skip to content

DES(Data Encryption Standard)

DES简介

DES(Data Encryption Standard,数据加密标准) 是一种对称密钥加密算法,由IBM在1970年代为美国国家标准局(NBS,现为NIST)设计,1977年被采纳为联邦信息处理标准(FIPS)。DES曾广泛用于数据的保护和加密,是当时最流行的加密方法之一。

工作原理

DES 是一种 对称加密算法,这意味着同一个密钥用于加密和解密。它采用 块加密 的方式,将数据分成固定大小的块(通常是64位),然后对每个块进行加密操作。

1. 密钥

DES 密钥由 8 个字节(每个字节 8 位)组成,其中每个字节的最后一位是校验位。校验位是这样设置的:

每个字节的前 7 位用于加密(即 56 位有效加密位)。 每个字节的第 8 位(最高位)是奇偶校验位,确保每个字节中有奇数个“1”。

校验位计算:

对于每个 8 位字节,计算其前 7 位中“1”的个数。 如果前 7 位的“1”的个数是偶数,则校验位(第 8 位)设为 1,使得整个字节有奇数个“1”。 如果前 7 位的“1”的个数是奇数,则校验位设为 0,保持奇数个“1”。

2. 初始置换 (Initial Permutation, IP)

加密开始前,明文块首先通过一个初始置换,将数据的位重新排列。这个操作虽然不增加安全性,但被设计成算法的固定部分。

3. 16轮的Feistel结构

DES 采用了 Feistel 网络结构,每一轮都对数据块进行复杂的操作:

  • 数据分组:将64位的数据块分为左右两部分,各32位。
  • 轮函数 (Round Function, F-Function):每一轮使用的 48 位子密钥和右半部分结合,经过一系列操作生成新的数据块。
    • 扩展置换 (Expansion Permutation, E-Box):右半部分由32位扩展为48位。
    • 与子密钥异或:扩展后的数据块与本轮的子密钥进行异或操作。
    • S-盒替换 (Substitution, S-Box):异或后的48位数据被分为8组,每组6位,通过S-盒进行非线性替换,将6位压缩为4位。
    • P-盒置换 (Permutation, P-Box):经过S-盒替换后的32位数据再经过P-盒置换,以增加混淆度。
  • 左右交换:将本轮输出的左右部分交换,为下一轮输入做准备。

4. 逆初始置换 (Inverse Initial Permutation, IP-1)

所有16轮完成后,将数据块经过一次逆初始置换,恢复数据的原始位序。

c语言实现示例

#include <stdio.h>
#include <stdlib.h>
#define LB32_MASK 0x00000001
#define LB64_MASK 0x0000000000000001
#define L64_MASK 0x00000000ffffffff
#define H64_MASK 0xffffffff00000000
/* Initial Permutation Table */
static char 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
};
/* Inverse Initial Permutation Table */
static char PI[] = {
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
};
/*Expansion table */
static char 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
};
/* Post S-Box permutation */
static char 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
};
/* The S-Box tables */
static char S[8][64] = {{
/* 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
}};
/* Permuted Choice 1 Table */
static char 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
};
/* Permuted Choice 2 Table */
static char 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
};
/* Iteration Shift Array */
static char iteration_shift[] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
/**
*
* @param input 加密的明文
* @param key 加密的密码
* @param mode 'd'是解密'e'为加密
* @return
*/
uint64_t des(uint64_t input, uint64_t key, char mode) {
int i, j;
// 用于表示行和列的变量,用于S-盒选择
char row, column;
// 28位变量,用于存储密钥的C和D部分
uint32_t C = 0;
uint32_t D = 0;
// 32位变量,用于存储L和R部分以及函数结果
uint32_t L = 0;
uint32_t R = 0;
uint32_t s_output = 0;
uint32_t f_function_res = 0;
uint32_t temp = 0;
// 48位变量,用于存储子密钥和扩展后的R部分
uint64_t sub_key[16] = {0};
uint64_t s_input = 0;
// 56位变量,用于存储置换选择1和2的结果
uint64_t permuted_choice_1 = 0;
uint64_t permuted_choice_2 = 0;
// 64位变量,用于存储初始置换、DES结果和输出前的中间结果
uint64_t init_perm_res = 0;
uint64_t des_result = 0;
uint64_t pre_output = 0;
// 初始置换(IP置换)
for (i = 0; i < 64; i++) {
init_perm_res <<= 1;
init_perm_res |= (input >> (64-IP[i])) & LB64_MASK;
}
// 将初始置换结果拆分为左右两部分
L = (uint32_t) (init_perm_res >> 32) & L64_MASK;
R = (uint32_t) init_perm_res & L64_MASK;
// 生成16轮子密钥
for (i = 0; i < 56; i++) {
permuted_choice_1 <<= 1;
permuted_choice_1 |= (key >> (64-PC1[i])) & LB64_MASK;
}
// 将置换选择1的结果分为C和D部分
C = (uint32_t) ((permuted_choice_1 >> 28) & 0x000000000fffffff);
D = (uint32_t) (permuted_choice_1 & 0x000000000fffffff);
// 进行16轮左移和生成子密钥
for (i = 0; i < 16; i++) {
for (j = 0; j < iteration_shift[i]; j++) {
// 左移操作
C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27));
D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27));
}
// 将C和D合并
permuted_choice_2 = 0;
permuted_choice_2 = (((uint64_t) C) << 28) | (uint64_t) D;
// 生成子密钥
sub_key[i] = 0;
for (j = 0; j < 48; j++) {
sub_key[i] <<= 1;
sub_key[i] |= (permuted_choice_2 >> (56-PC2[j])) & LB64_MASK;
}
}
// 16轮加密/解密过程
for (i = 0; i < 16; i++) {
// f函数的输入是R部分的扩展结果
s_input = 0;
for (j = 0; j < 48; j++) {
s_input <<= 1;
s_input |= (uint64_t) ((R >> (32-E[j])) & LB32_MASK);
}
// 根据模式选择子密钥进行异或操作
if (mode == 'd') {
// 解密模式
s_input = s_input ^ sub_key[15-i];
} else {
// 加密模式
s_input = s_input ^ sub_key[i];
}
// S-盒替换
for (j = 0; j < 8; j++) {
row = (char) ((s_input & (0x0000840000000000 >> (6*j))) >> (42-6*j));
row = (row >> 4) | (row & 0x01);
column = (char) ((s_input & (0x0000780000000000 >> (6*j))) >> (43-6*j));
s_output <<= 4;
s_output |= (uint32_t) (S[j][16*row + column] & 0x0f);
}
// P置换
f_function_res = 0;
for (j = 0; j < 32; j++) {
f_function_res <<= 1;
f_function_res |= (s_output >> (32 - P[j])) & LB32_MASK;
}
// L和R部分交换
temp = R;
R = L ^ f_function_res;
L = temp;
}
// 将L和R部分合并成64位结果
pre_output = (((uint64_t) R) << 32) | (uint64_t) L;
// 逆初始置换(PI置换)
for (i = 0; i < 64; i++) {
des_result <<= 1;
des_result |= (pre_output >> (64-PI[i])) & LB64_MASK;
}
// 返回最终加密/解密结果
return des_result;
}
int main(int argc, char *argv[])
{
char mode = 'e';
uint64_t input = 0x3132333435363738;
uint64_t key = 0x3030303030303030;
printf("%016llX\n", des(input, key, mode));
return 0;
}

3DES简介

3DES(Triple Data Encryption Standard,三重数据加密标准)是一种对称密钥加密算法,它是在传统 DES(Data Encryption Standard)基础上改进而来的。3DES 的设计目的是增强 DES 的安全性,以应对由于 DES 密钥长度较短(56 位)而带来的安全性不足问题。

3DES的工作原理

3DES 的核心思想是通过多次应用 DES 算法来增加安全性。具体来说,3DES 使用三次 DES 加密过程来加密数据,这也是它名字的由来。3DES 主要有两种加密模式:

1. EDE 模式(加密-解密-加密)

EDE 模式是最常用的 3DES 模式:

  • 步骤 1: 使用第一个密钥 K1 对数据进行 DES 加密。
  • 步骤 2: 使用第二个密钥 K2 对步骤 1 的输出进行 DES 解密。
  • 步骤 3: 使用第三个密钥 K3 对步骤 2 的输出进行 DES 加密,得到最终的密文。

2. EEE 模式(加密-加密-加密)

EEE 模式也可以使用,但较少见:

  • 步骤 1: 使用第一个密钥 K1 对数据进行 DES 加密。
  • 步骤 2: 使用第二个密钥 K2 对步骤 1 的输出进行 DES 加密。
  • 步骤 3: 使用第三个密钥 K3 对步骤 2 的输出进行 DES 加密,得到最终的密文。

3DES的密钥长度

3DES 可以使用两种密钥长度:

  • 2 密钥 3DES(2-key 3DES)K1K3 使用相同的密钥,因此有效密钥长度为 112 位(两个 56 位密钥)。
  • 3 密钥 3DES(3-key 3DES)K1K2K3 都使用不同的密钥,因此有效密钥长度为 168 位(三个 56 位密钥)。

3DES的优缺点

优点:

  • 增强的安全性:3DES 通过三次应用 DES 加密算法,大大提高了对暴力破解的抵抗能力,扩展了密钥空间。
  • 向后兼容:3DES 继承了 DES 的设计思想,因此可以在需要兼容传统 DES 系统的场合使用。

缺点:

  • 速度较慢:由于需要进行三次 DES 运算,3DES 的加密和解密速度比单次 DES 慢了三倍,不适合资源受限的设备。
  • 密钥管理复杂:3DES 需要管理 2 或 3 个密钥,密钥管理比 DES 更复杂。

3DES的使用场景

由于其安全性和向后兼容性,3DES 曾在金融服务、支付系统和某些政府应用中得到广泛使用。然而,随着计算能力的不断提升和更安全的算法(如 AES)的出现,3DES 在现代应用中逐渐被淘汰。