一、简要介绍
DES算法采用位操作(如置换、选择、异或等)和S盒替换等方式,将64位的明文和56位的密钥通过16轮迭代运算,生成64位的密文。解密过程则是加密过程的逆向操作。
二、困难点主要有:
1. 复杂的运算步骤:DES算法包括了多种不同的运算步骤,如初始置换、扩展置换、S盒替换、P盒置换等,每一步都有其特定的逻辑和功能,需要花费一些时间去理解和记忆。
2. 位操作:DES算法大量使用了位操作,如异或、左移、右移等,对位操作不熟悉的人来说可能会感到有些困难。
3. 密钥的生成和处理:DES算法的密钥生成包括置换选择、左移、右移等步骤,理解这些步骤并掌握其原理是学习DES算法的一个挑战。
三、DES算法的重要部分主要有:
1. 初始置换和逆初始置换:这是DES算法的第一步和最后一步,它们保证了算法的对称性。
2. 扩展置换:这一步将32位数据扩展为48位,为后续与密钥进行异或运算做准备。
3. S盒替换:这是DES算法的核心部分,它通过一系列复杂的操作将48位的数据转化为32位,增加了破解的难度。
4. P盒置换:这一步将S盒替换的结果进行再次置换,增加了算法的混淆性。
5. 密钥的生成:这是每轮迭代中都会用到的部分,密钥的生成过程直接影响到整个加密的安全性。
理解上述的各个部分,并掌握它们之间的联系,是理解和掌握DES算法的关键。
四、代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//DES算法把64位的明文输入块变为64位的密文输出块
使用的密钥也是64位,其功能是把输入的64位数据块按位重新组合,
并把输出分为L0、R0两部分,每部分各长32位,
其置换规则如下
const static char IP_Table[64] = {
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 };
//逆置换规则如下表
const static char IP_Table2[64] = {
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 };
int 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 } },
s2[4][16] = { { 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[4][16] = { { 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[4][16] = { { 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, 3 },{ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } },
s5[4][16] = { { 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[4][16] = { { 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][16] = { { 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[4][16] = { { 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 } };
const static char e[48] = { 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 }; //E盒
int pp[32] = { 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 }; //P盒
const static char pc_1[64] = { 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 };
const static char pc_2[64] = { 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 };
const static char moveleft[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
void print2(unsigned char n) {
char i = 0;
int a;
for (i = 0; i < 8; i++) {
a = n >> (7 - i);
a = a & 1;
printf("%d", a);
}
}
void unit642array(unsigned long long int a, unsigned char t[]) {
for (int i = 0; i < 64; i++)
t[i] = (a >> (63 - i)) & 0x1;
}
unsigned long long int array2unit64(unsigned char t[]) {
unsigned long long int a = 0;
for (int i = 0; i < 64; i++) {
a = a << 1;
a = a ^ t[i];
}
return a;
}
void unit648array(unsigned long long int M, unsigned char p[]) {
int i, j;
unsigned long long int a = 0;
for(i = 0; i < 8; i++)
for (j = i * 8; j < 8 * (i + 1); j++) {
a = (M >> (63 - j)) & 0x1;
p[i] = p[i] << 1;
p[i] = p[i] ^ a;
}
}
unsigned long long int array8unit64(unsigned char p[]) {
int i;
unsigned long long int m = 0;
for (i = 0; i < 8; i++) {
m = m << 8;
m = m ^ p[i];
}
return m;
}
void unit1array8(unsigned char p,unsigned char n[]) {
int i;
for (i = 7; i >= 0; i--) {
n[i] = p & 1;
p = p >> 1;
}
}
void print2_64(unsigned long long int n) {
unsigned char p[8];
int i;
unit648array(n, p);
for (i = 0; i < 8; i++) {
print2(p[i]);
}
}
void initIPtable(unsigned long long int T[][256]) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 256; j++) {
unsigned long long int a = j;
a = a << ((7 - i) * 8);
unsigned char s1[64];
unsigned char s2[64] = { 0 };
unit642array(a, s1);
for (int k = 0; k < 64; k++)
s2[k] = s1[IP_Table[k] - 1];
a = array2unit64(s2);
T[i][j] = a;
}
}
void initIPtable2(unsigned long long int T[][256]) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 256; j++) {
unsigned long long int a = j;
a = a << ((7 - i) * 8);
unsigned char s1[64];
unsigned char s2[64] = { 0 };
unit642array(a, s1);
for (int k = 0; k < 64; k++)
s2[k] = s1[IP_Table2[k] - 1];//先把a拆成64个1字节的数,再根据IP_Table[k] 求出在s2数组中k位置的值
a = array2unit64(s2);
T[i][j] = a;
}
}
//IP置换
unsigned long long int IP(unsigned long long int T[][256], unsigned char p[], unsigned long long int m) {
int i;
for (i = 0; i < 8; i++)
m= m ^ T[i][p[i]];
return m;
}
//扩展运算(E盒)
void ee(unsigned char p[], unsigned long long int R) {
int i, j;
unsigned char t[64], a = 0;
unit642array(R, t);
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++)
if (j != 0 && j != 1) {
a = t[e[6 * i + j - 2] - 1];
p[i] = (p[i] << 1) ^ a;
}
}
}
//选择压缩运算(S盒)将48bits分为8*6bit 根据‘8个6进4出s盒’进行压缩
//ps:对第几个6bit进行压缩就使用第几个s盒
void sbox(unsigned char p[]) {
unsigned char i, j, n[8];
int k;
for (k = 0; k < 8; k++) {
unit1array8(p[k], n);
i = n[2];
i = i << 1;
i = i ^ n[7];
j = n[3];
j = j << 1;
j = j ^ n[4];
j = j << 1;
j = j ^ n[5];
j = j << 1;
j = j ^ n[6];
switch (k) {
case 0:
p[0] = s1[i][j]; break;
case 1:
p[1] = s2[i][j]; break;
case 2:
p[2] = s3[i][j]; break;
case 3:
p[3] = s4[i][j]; break;
case 4:
p[4] = s5[i][j]; break;
case 5:
p[5] = s6[i][j]; break;
case 6:
p[6] = s7[i][j]; break;
case 7:
p[7] = s8[i][j]; break;
};
}
}
//置换运算(p盒)
//置换之后就将结果与L0/H0进行异或
unsigned long long int pchange(unsigned char p[]) {
int i, j;
unsigned char t[64], t2[64] = {0};
unsigned long long int m2 = 0;
for (i = 0; i < 8; i++) {
m2 = m2 << 4;
m2 = m2 ^ p[i];
}
m2 = m2 << 32;
unit642array(m2, t);
for (j = 0; j < 32; j++)
t2[j] = t[pp[j] - 1];
return array2unit64(t2);
}
//异或运算
unsigned long long int diffor(unsigned long long int m1, unsigned long long int m2) {
return m1 ^ m2;
}
void addzero(unsigned char kk[]) {
int i;
unsigned long long int k;
k = array8unit64(kk);
for (i = 0; i < 8; i++) {
kk[i] = k >> (57 - i * 7) << 1;
}
}
unsigned long long int changepc_1(unsigned long long int k) {
unsigned char t[64],t2[64];
int i;
unit642array(k, t);
for (i = 0; i < 64; i++)
t2[i] = t[pc_1[i] - 1];
return array2unit64(t2);
}
unsigned long long int changepc_2(unsigned long long int k) {
unsigned char t[64], t2[48];
int i;
unit642array(k, t);
for (i = 0; i < 48; i++)
t2[i] = t[pc_2[i] - 1];
return array2unit64(t2);
}
//密钥扩展
unsigned long long int producek(unsigned long long int k, int a) {
int i;
unsigned char t[64] = {0}, k2, t2[64] = {0};
unsigned long long int L, R;
k = changepc_1(k);
L = k >> 36 << 36;
R = k << 28;
for (i = 0; i < moveleft[a]; i++) {
unit642array(R, t);
k2 = t[0];
R = R << 1;
unit642array(R, t2);
t2[27] = t[0];
R = array2unit64(t2);
unit642array(L, t);
k2 = t[0];
L = L << 1;
unit642array(L, t2);
t2[27] = t[0];
L = array2unit64(t2);
}
k = L ^ (R >> 28);
k = changepc_2(k);
k = k >> 16 << 16;
return k;
}
int main() {
int i, j, a;
unsigned long long int M, k, m = 0, R, L, m2 = 0, m3;
unsigned char p[8], p2[8] = {0}, p3[8] = {0}, MM[9] = {0}, kk[9] = {0}, s1[64], s2[8] = {0};
unsigned long long int T[8][256] = {0};
printf("请输入明文M:");
scanf("%s", MM);
M = array8unit64(MM);
printf("请输入密钥k:");
scanf("%s", kk);
addzero(kk);
k = array8unit64(kk);
initIPtable(T);
unit648array(M, p);
m = IP(T, p, m);
L = m >> 32 <<32;
R = m << 32;
for (j = 0; j < 16; j++) {
k = producek(k, j);
ee(p2, R);
unit642array(k, s1);
for (i = 0; i < 8; i++) {
for (a = 0; a < 6; a++) {
s2[i] = s2[i] << 1;
s2[i] = s2[i] ^ s1[i * 6 + a];
}
p2[i] = p2[i] ^ s2[i];
}
sbox(p2);
m2 = pchange(p2);
m3 = diffor(m2, L);
R = m3;
L = R;
}
m3 = R;
R = L;
L = m3;
m = L ^ (R >> 32);
initIPtable2(T);
IP(T, p, m);
printf("密文为:");
print2_64(m);
}
五、实现代码后得出以下心得体会:
困难点:
1. Bit Manipulation: DES算法涉及到大量的位操作,包括位移、异或等操作,对理解和掌握位操作有较高的要求。
2. 密钥扩展: 密钥扩展是DES算法的关键部分,需要将64位的密钥扩展为16个48位的子密钥,这一过程中涉及到PC-1和PC-2选择、左移等操作,实现起来相对复杂。
3. S-boxes substitution: 这是DES算法的核心部分,需要对数据进行非线性替换,这一过程中涉及到多个S盒,每个S盒都有不同的替换规则,理解和实现起来较为困难。
4. Feistel结构: DES算法采用Feistel结构,这导致每一轮的加密过程都需要进行分组、扩展、替换、置换等多个步骤,实现起来需要对整个流程有清晰的理解。
5. Initial and final permutations: DES算法的开始和结束都需要进行置换操作,这两个置换的规则不同,需要对这两个过程有清晰的理解。
六、总结:
1. 密钥扩展: 将64位的密钥扩展为16个48位的子密钥,这是DES算法加密过程中使用的密钥。
2. Feistel结构: DES算法的每一轮加密都遵循Feistel结构,包括分组、扩展、替换和置换等步骤。
3. S-boxes substitution: 这是DES算法的核心部分,通过对数据进行非线性替换,增加了加密的复杂度。
4. Initial and final permutations: 这两个操作在DES算法的开始和结束进行,分别对数据进行置换,保证了数据的混乱和扩散。
5. 16 rounds of encryption: DES算法进行16轮加密,每一轮加密都使用不同的子密钥,加大了破解的难度。
ps:需要注意的是,DES算法虽然历史悠久,是一种经典的对称加密算法,但由于其密钥长度较短,容易受到暴力破解的攻击,因此在需要更高安全性的场合,推荐使用AES等更安全的加密算法。