DES简介

DES算法为密码体制中的对称密码体制,又被称为美国数据加密标准。

DES是一个分组加密算法,典型的DES以64位为分组对数据加密,加密和解密用的基本上是同一个算法。其密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个1),分组后的明文组和56位的密钥按位替代或交换的方法形成密文组。

算法大致流程

大概流程如下

具体一些的如下

原理和实现

由第二节的流程图,我们大概知道了,DES加密算法大概流程为

IP置换 => 16轮迭代 =>IP逆置换 三个步骤

其中涉及到F轮函数的设计以及密钥生成方法,下面将分别介绍。

IP置换

将64位二进制明文按照下表进行置换,经过置换后,明文的第i位变成了原来的IP_1[i]位。


置换之后再把64位明文分为L0和R0两部分,各32位。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool data[65],L_0[65],R_0[65]; // 明文以及打乱之后分开的L0和R0

int IP_1[65] = // IP初始置换表
{
0, 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
};

void IP_Init_Trans()
{
bool tmp[65];
for(int i=1;i<=64;i++)tmp[i] = data[i];
for(int i=1;i<=64;i++)data[i] = tmp[IP_0[i]];
for(int i=1;i<=32;i++)L_0[i] = data[i],R_0[i] = data[i + 32];
}

轮函数

轮函数分为四步:

E扩展

扩展置置换目标是IP置换后获得的右半部分R_0,将32位输入扩展为48位(分为4位×8组)输出。

由流程图可知这个函数只对R_0用
,而L_0是直接赋值的。

E扩展置换表为

可以发现,这个置换表的原理就是,把32bit数据分成8组,每组4bit,把每个4bit数据最左边添加上其上一组4bit数据的第四位,每个4bit数据最右边添加上其下一组数据的第一位,其中第一组和最后一组是相邻的。这样就把8 * 4bit = 32bit数据转变成了8 * 6bit = 48bit数据。

轮密钥异或

扩展置换之后,右半部分数据R_0变为48位,与轮密钥进行异或,这里只需要知道轮密钥是由密钥生成函数产生的,其长度也为48bit,密钥生成函数后面会提到。

S盒压缩

将异或之后的48位R_0分成8组,每组长度为6bit,将它们分别输入到S_1到S_8这8个盒中,每个盒产生4位输出,最后将输出拼接成4 * 8 = 32bit数据。

具体方法为:

把6bit数据首尾2位拿出来,中间4位拿出来,拼接转化成10进制数,假设分别为a,b,输出就是第S_i盒的第a行第b列。再把这个十进制数转化为4位2进制数,就实现了S盒的压缩功能。

P置换

最后再进行一次P盒置换

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
int E[33] =
{
0, 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
};

int S[10][5][17] =
{
{ // 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}
}
};

int P[33] =
{
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
};

int E_R0[50];

void E_Extent()
{
for(int i=1;i<=48;i++)
E_R0[i] = R0[E[i]];

/*for(int i=0;i<8;i++)
{
if(i == 0)E_R0[i + 1] = R0[32],E_R0[i + 6] = R0[(i + 1) * 4 + 1];
else if(i == 7)E_R0[i * 6 + 1] = R0[(i - 1) * 4 + 4],E_R0[i * 6 + 6] = R0[1]; // 特判一下
else E_R0[i * 6 + 1] = R0[(i - 1) * 4 + 4], E_R0[i * 6 + 6] = R0[(i + 1) * 4 + 1];
for(int j=2;j<=5;j++) E_R0[i * 6 + j] = R0[i * 4 + j - 1];
}*/
}

void Key_Xor()
{
for(int i=1;i<=48;i++)E_R0[i] ^= K0[i];
}

void S_Compact()
{
for(int i=0;i<8;i++)
{
int a = E_R0[i * 6 + 1] << 1 + E_R0[i * 6 + 6];
int b = E_R0[i * 6 + 2] << 3 + E_R0[i * 6 + 3] << 2 + E_R0[i * 6 + 3] << 1 + E_R0[i * 6 + 4];
int c = S[i][a][b];
for(int j=4;j>=1;j--)
{
R0[i * 4 + j] = c % 2;
c /= 2;
}
}
}

void P_Rep()
{
int tmp[33];
for(int i=1;i<=32;i++)tmp[i] = R0[i];
for(int i=1;i<=32;i++)R0[i] = tmp[P[i]];
}

void f()
{
E_Extent();
Key_Xor();
S_Compact();
P_Rep();
}

IP逆置换

16轮迭代后将L16与R16合并,形成64位二进制,然后按照下表进行逆初始置换,得到最终的64位密文。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int IP2[65] = 
{
0, 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
};

void IP_RevTrans()
{
int tmp[65];
for(int i=1;i=32;i++)tmp[i] = L16[i];
for(int i=33;i<=64;i++)tmp[i] = R16[i - 32];
for(int i=1;i<=64;i++)data[i] = tmp[IP2[i]];
}

密钥生成

之前只介绍了关于密钥的使用,但是第k轮的密钥是如何获得的并没有说明。

密钥生成流程如下

将初始的64位密钥按顺序编号,然后填充为8*8矩阵,最后一列作为奇偶校验位(也就是8,16,24,32,40,48,56和64这8位),其他的56位组成初始56位密钥。

随后将初始56位密钥按下图的置换表进行置换,打乱密钥顺序

发现从64位变成了56位,我们把这56位分成两份Ci和Di,每份28位,按下方的移位次数表进行移位

每一轮移位以后我们都可以将C和D拼起来获得一个56位密钥Ki,再按照下表对密钥进行压缩,从56位变成48位,就可以用到轮函数的计算中了。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int Key[65], Key_Trans[65], C[65], D[65];
int Key_mov[17] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};

int Key_Init[57] = // 密钥初始置换表
{
0, 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
};
int Key_comp[50] = // 第二次压缩置换表
{
0, 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
};
void Key_InitTrans() // 密钥初始置换算法
{
for (int i=1;i<=56;i++)Key_trans[i] = Key[Key_Init[i]];

for (int i=0;i<=28;i++)C[i] = Key_trans[i];
for (int i=29;i<=56;i++)D[i - 28] = Key_trans[i];

int tmp_1, tmp_2, tmp_3, tmp_4;//存第一位
for (int i = 0; i < 16; ++i)
{
if (Key_mov[i] == 1) // 左移一位
{
tmp_1 = C[1], tmp_2 = D[1];
for (j=1;j<=27;j++)C[j] = C[j + 1], D[j] = D[j + 1];
C[28] = tmp_1, D[28] = tmp_2;
}
else // 左移两位
{
tmp_1 = C[1], tmp_2 = C[2], tmp_3 = D[1], tmp_4 = D[2];
for (int j=1;j<=26;j++)C[j] = C[j + 2], D[j] = D[j + 2];
C[26] = tmp_1, C[27] = tmp_2, D[26] = tmp_3, D[27] = tmp_4;
}
}
for (int i=1;i<=28;i++) Key_trans[i] = C[i];
for (int i=29;i<=56;i++) Key_trans[i] = D[i - 28]; // 拼起来
for (int i=1;i<=48;i++) Key[i] = Key_trans[Key_comp[i]]; //第二次置换
}

解密过程

DES算法的解密和加密是一个原理,算法也基本类似,只有少数地方不同

  • 若加密时子密钥的使用顺序是K1, K2…K16,那么解密时顺序就是K16, K15…K1

  • 加密生成密钥时是循环左移,解密生成密钥时是循环右移

完整的轮子(网上嫖的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
//经过上述对DES算法每一步的深入研究分析,在理论上对DES算法的实现有了较为清晰的思路,笔者将依据上述理论分析,在Windows10操作系统下,利用Codeblocks开发环境,采用C++11标准对DES算法进行代码实现。
//笔者特别说明,为了简便话,笔者使用随机数来生成算法所需的各个表格,并且在加密和解密过程中用到的密钥相同,参照表格相同。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<ctime>
using namespace std;

char plaintext[8];//明文8字符,64位
char key[16];//秘钥,16个16进制数,64位
char ciphertext[16];//密文,16个16进制数,64位

int PC_1[8][7];//64位秘钥转成56位秘钥所用表
int left_move_table[16];//循环左移参照表
int PC_2[8][6];//cidi56位秘钥转成48位秘钥ki所用表
int E_bit_selection_table[8][6];//在f函数执行期间,转换Ri用到的参照表

int IP_1[8][8];//initial permutation ,64位数据第一次变换参照的表
int S[8][4][16];//8个S盒
int sub_key[17][48];//16个子秘钥,k1到k16
int P[8][4];//
int IP_2[8][8];//最后一次变换所参照的表
int L[17][32],R[17][32];//数据加密阶段,产生16对数据块1到16分别存放

void init();//初始化各个参照表
void hex_bin(char x,int tmp[]);//16进制数x转成4位二进制
void bin_hex(int tmp[4],char &c);//4位2进制转16进制,结果存放在字符c中
void char_to_bin(char c,int tmp[]);//把一个ADCII码字符转成2进制8位存储到tmp
void circle_left_move(int a[],int step);//循环左移,这里只有28位的秘钥,左移step位
void make_sub_key();//产生子秘钥

void exclusive_or(int a[],int b[],int n,int result[]);//异或,a数组,b数组表示的两个n位二进制数进行异或
void make_random(int n,int s[]);//产生n个随机数,放在数组a中

void f(int n,int P_end[32]);//得到的f存到P_end数组中
void des();//执行DES算法
void decode();//解密

void go();//程序控制

int main()
{
go();
return 0;
}

//产生n个互不相同的数分别是1-n
void make_random(int n,int s[]){
int a[64];
int index=0;
for(int i=0;i<n;i++){
a[i]=i+1;
}
srand(unsigned(time(0)));//播种子
for(int i=0;i<n;){
index=rand()%n;
if(a[index]!=0){
s[i]=a[index];
a[index]=0;
i++;
}
}
}

void init(){
int s[64];//存放产生的随机数
int cnt=0;
//初始化PC_1表(64位秘钥转56位秘钥参照表)
make_random(64,s);
cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<7;j++){
PC_1[i][j]=s[cnt++];
}
}

//初始化循环左移表left_move_table
for(int i=0;i<16;i++){
left_move_table[i]=rand()%6+1;//这里假设循环左移1到6位
}

//初始化PC_2表(56位秘钥转48位秘钥参照表)
make_random(48,s);
cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<6;j++){
PC_2[i][j]=s[cnt++];
}
}

//初始化IP_1表(64位原始数据转成新的64位数据块
make_random(64,s);
cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
IP_1[i][j]=s[cnt++];
}
}

//初始化E_bit_selection_table,在f函数执行期间对Ri转换下
//32位扩到48位,只能在1-32选取
for(int i=0;i<8;i++){
for(int j=0;j<6;j++){
E_bit_selection_table[i][j]=rand()%32+1;
}
}

//初始化S盒
for(int i=0;i<8;i++){
for(int j=0;j<4;j++){
make_random(16,s);
cnt=0;
for(int k=0;k<16;k++){
s[k]-=1;
}
for(int k=0;k<16;k++){
S[i][j][k]=s[cnt++];
}
}
}

//初始P,8个盒子已经搞完了一遍
make_random(32,s);
cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<4;j++){
P[i][j]=s[cnt++];
}
}

//初始化最后一个表IP_2
make_random(64,s);
cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
IP_2[i][j]=s[cnt++];
}
}
}

//一个十六进制数转成4位二进制数
void hex_bin(char x,int tmp[]){
int a,b,c,d;
if('0'==x){a=0;b=0;c=0;d=0;}
else if('1'==x){a=0;b=0;c=0;d=1;}
else if('2'==x){a=0;b=0;c=1;d=0;}
else if('3'==x){a=0;b=0;c=1;d=1;}
else if('4'==x){a=0;b=1;c=0;d=0;}
else if('5'==x){a=0;b=1;c=0;d=1;}
else if('6'==x){a=0;b=1;c=1;d=0;}
else if('7'==x){a=0;b=1;c=1;d=1;}
else if('8'==x){a=1;b=0;c=0;d=0;}
else if('9'==x){a=1;b=0;c=0;d=1;}
else if('a'==x || 'A'==x){a=1;b=0;c=1;d=0;}
else if('b'==x || 'B'==x){a=1;b=0;c=1;d=1;}
else if('c'==x || 'C'==x){a=1;b=1;c=0;d=0;}
else if('d'==x || 'D'==x){a=1;b=1;c=0;d=1;}
else if('e'==x || 'E'==x){a=1;b=1;c=1;d=0;}
else if('f'==x || 'F'==x){a=1;b=1;c=1;d=1;}
tmp[0]=a;tmp[1]=b;tmp[2]=c;tmp[3]=d;
}

void bin_hex(int tmp[4],char &c){
int sum=tmp[0]*8+tmp[1]*4+tmp[2]*2+tmp[3]*1;
if(sum>=0 && sum<=9){
c=sum+'0';
}
else{
if(10==sum) c='a';
else if(11==sum) c='b';
else if(12==sum) c='c';
else if(13==sum) c='d';
else if(14==sum) c='e';
else if(15==sum) c='f';
}

}

//一个字符转成8位二进制数
void char_to_bin(char c,int tmp[]){
int n=(int)c;//把ASCII码字符强制转为整数
int cnt=7;
while(n/2){
tmp[cnt--]=n%2;
n/=2;
}
tmp[cnt]=n;
for(int i=0;i<cnt;i++){
tmp[i]=0;
}
}

//一个数转成4位的二进制数
void number_to_bin(int n,int tmp[]){
int cnt=3;
while(n/2){
tmp[cnt--]=n%2;
n/=2;
}
tmp[cnt]=n;
for(int i=0;i<cnt;i++){
tmp[i]=0;
}
}

void circle_left_move(int a[],int step){
int tmp[28];
int cnt=0;
for(int i=step-1;i<28;i++){
tmp[cnt++]=a[i];
}
for(int i=0;i<step;i++){
tmp[cnt++]=a[i];
}
for(int i=0;i<28;i++){
a[i]=tmp[i];
}
}

void make_sub_key(){
int kkey[64];//把16个16进制的数搞成64个二进制的数,并存储在kkey数组中
int kkey_cnt=0;
//秘钥key中的16个16进制数搞成二进制数共64位存到kkey数组中
for(int i=0;i<16;i++){
int tmp[4];
hex_bin(key[i],tmp);
for(int j=0;j<4;j++){
kkey[kkey_cnt++]=tmp[j];
}
}

int K[56];//原秘钥64位搞成56位放在K里面
int K_cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<7;j++){
K[K_cnt++]=kkey[PC_1[i][j]-1];//参照表PC_1来搞,把64位搞成56位秘钥
}
}
int c[17][28],d[17][28];
//64位秘钥搞成56位秘钥后,拆成两半,分别给c[0]和d[0]
for(int i=0;i<28;i++){
c[0][i]=K[i];
d[0][i]=K[i+28];
}

//循环左移16次,得到16对ci,di,存到c[17][28],d[17][28]
for(int i=0;i<16;i++){
int tmp_c[28],tmp_d[28];//暂时存储c[i]和d[i]
for(int j=0;j<28;j++){
tmp_c[j]=c[i][j];
tmp_d[j]=d[i][j];
}
//c[i],d[i]都循环左移left_move_table[i]位
circle_left_move(tmp_c,left_move_table[i]);
circle_left_move(tmp_d,left_move_table[i]);
//c[i],d[i]循环左移后,就变成了c[i+1],d[i+1]
for(int j=0;j<28;j++){
c[i+1][j]=tmp_c[j];
d[i+1][j]=tmp_d[j];
}
}

//对16对cidi56位秘钥用PC_2表转成48位秘钥,存到sub_key数组中
for(int i=0;i<16;i++){
int tmp[56];//暂存c[i+1],d[i+1]组成56位秘钥
for(int j=0;j<28;j++){
tmp[j]=c[i+1][j];
tmp[j+28]=d[i+1][j];
}

//56位转成48位
int cnt=0;
for(int j=0;j<8;j++){
for(int k=0;k<6;k++){
sub_key[i+1][cnt++]=tmp[PC_2[j][k]-1];
}
}
}
}

void exclusive_or(int a[],int b[],int n,int result[]){
for(int i=0;i<n;i++){
if(a[i]==b[i]){
result[i]=0;
}
else{
result[i]=1;
}
}
}


void f(int n,int P_end[32]){
int E[48];
int E_cnt=0;
//Ri通过E_bit_selection_table表转换一下,存到E中
for(int i=0;i<8;i++){
for(int j=0;j<6;j++){
E[E_cnt++]=R[n-1][E_bit_selection_table[i][j]-1];
}
}
int tmp_sub_key[48];//暂存sub_key[n],一维数组便于操作
for(int i=0;i<48;i++){
tmp_sub_key[i]=sub_key[n][i];
}

int exclusive_or_result[100];//存放异或结果的数组
exclusive_or(E,tmp_sub_key,48,exclusive_or_result);

//把子秘钥sub_key[i]与E异或后的结果exclusive_or_result,48位拆成8份
//每一份有6位,分别对每一份进行S盒转变
int temp_result[8][6];
int temp_result_cnt=0;
for(int i=0;i<48;i++){
if(i%6==0 && i!=0){
temp_result_cnt++;
}
temp_result[temp_result_cnt][i%6]=exclusive_or_result[i];
}

int row;//是S盒中对应的行
int col;//在S盒中对于的列
int S8_end[32];//8个盒搞完之后的结果
int S8_end_cnt=0;//8个S盒过一遍之后的结果计数
for(int i=0;i<8;i++){
row=temp_result[i][0]*2+temp_result[i][5]*1;
col=temp_result[i][1]*8+temp_result[i][2]*4+temp_result[i][3]*2+temp_result[i][4]*1;
int tmp[4];//每一份6位变成4位后暂存tmp数组中
int number=S[i][row][col];//S盒中对应的那个数
number_to_bin(number,tmp);//S盒中对应的那个数转成二进制,暂存在tmp数组
for(int j=0;j<4;j++){
S8_end[S8_end_cnt++]=tmp[j];
}
}
//8个盒子过一遍之后,经P再搞一下,存到P_end里面,即f

int P_cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<4;j++){
P_end[P_cnt++]=S8_end[P[i][j]-1];
}
}
}

void des(){
int M[64];
int M_cnt=0;
//先把8个字符转成64位2进制数,存到M数组中
for(int i=0;i<8;i++){
int tmp[8];//存储每一个1字符对应的8位二进制
char_to_bin(plaintext[i],tmp);
for(int j=0;j<8;j++){
M[M_cnt++]=tmp[j];
}
}
int IP[64];//拿到8字符,转成64位二进制数据块后,安装IP_1表搞一下,搞成新的64位,存到IP数组中
int IP_cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
IP[IP_cnt++]=M[IP_1[i][j]-1];
}
}
//初始化L[0],R[0]为后续f函数准备
for(int i=0;i<32;i++){
L[0][i]=IP[i];
R[0][i]=IP[i+32];
}

//根据Ln=Rn-1,Rn=Ln-1+f(Rn-1,Kn)公式迭代,这里笔者对此公式更加简化如下
//Ln=Rn-1,Rn=f(n-1)
//下面16次循环后,可以求出L16,R16
for(int i=1;i<=16;i++){
for(int j=0;j<32;j++){
L[i][j]=R[i-1][j];//Ln=Rn-1
}
int P_end[32];
f(i,P_end);//Rn=f(n)+Ln-1,这里求出了f,下面异或下产生Rn

int L_tmp[32];
for(int j=0;j<32;j++){
L_tmp[j]=L[i-1][j];//这里开始还是写错了L_tmp[j]的j写成了i,这一点导致了极大的错误,加密和解密求的f全然不同,导致所有的L,R都不同
}

exclusive_or(P_end,L_tmp,32,P_end);
//上面异或的结果就是Rn
for(int j=0;j<32;j++){
R[i][j]=P_end[j];
}//至此,由f(n-1)终于求出了Rn
}
//求出的R16L16暂存在R16L16的数组中
int R16L16[64];
for(int i=0;i<32;i++){
R16L16[i]=R[16][i];
R16L16[i+32]=L[16][i];
}

int goal[64];//R16L16再过一遍IP_2
int goal_cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
goal[goal_cnt++]=R16L16[IP_2[i][j]-1];
}
}

//把得到的64位二进制转成16个16进制数
int tmp_goal[4];
char c;
int ciphertext_cnt=0;
for(int i=0;i<64;i++){
tmp_goal[i%4]=goal[i];
if((i+1)%4==0){
bin_hex(tmp_goal,c);
ciphertext[ciphertext_cnt++]=c;
}
}
}

//对加密之后的密文解密
void decode(){
//61个16进制数的密文先搞成64位,存到tmp1

int tmp1[64];
int tmp1_cnt=0;
for(int i=0;i<16;i++){
int tmp[4];//把一个16进制数转成4位的二进制数,存到tmp中
hex_bin(ciphertext[i],tmp);
for(int j=0;j<4;j++){
tmp1[tmp1_cnt++]=tmp[j];
}
}

//继续解密,先搞出R16L16
tmp1_cnt=0;
int R16L16[64];
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
R16L16[IP_2[i][j]-1]=tmp1[tmp1_cnt++];
}
}

//为了不定义更多的变量,下面求解过程中依然用加密过程中用到的变量
for(int i=0;i<32;i++){
R[16][i]=R16L16[i];
L[16][i]=R16L16[i+32];
}

//经过16次f函数迭代,求L0,R0
for(int i=16;i>=1;i--){
for(int j=0;j<32;j++){
R[i-1][j]=L[i][j];
}


int P_end[32];
f(i,P_end);
int R_tmp[32];
for(int j=0;j<32;j++){
R_tmp[j]=R[i][j];
}
exclusive_or(R_tmp,P_end,32,P_end);
for(int j=0;j<32;j++){
L[i-1][j]=P_end[j];
}

}
int L0R0[64];
for(int i=0;i<32;i++){
L0R0[i]=L[0][i];
L0R0[i+32]=R[0][i];
}

//求出起初的明文64位二进制表示,存到M数组
int M[64];//
int L0R0_cnt=0;
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
M[IP_1[i][j]-1]=L0R0[L0R0_cnt++];//这里有问题,看看!!!!!!!!!!!!!!!!!!!!!!!!
}
}

//把64位的M转成8个字符,并输出明文
int tmp_last[8][8];
int tmp_last_cnt=0;
//64位拆成8等份
for(int i=0;i<64;i++){
if(i%8==0 && i!=0){
tmp_last_cnt++;
}
tmp_last[tmp_last_cnt][i%8]=M[i];
}

int mark[8]={128,64,32,16,8,4,2,1};
for(int i=0;i<8;i++){
int x=0;
for(int j=0;j<8;j++){
x+=mark[j]*tmp_last[i][j];
}
plaintext[i]=(char)x;
}
}


void go(){
FILE *fp_plaintext;
FILE *fp_ciphertext;
FILE *fp_decode_text;
FILE *fp_key;

fp_plaintext=fopen("plaintext.txt","r");
fp_ciphertext=fopen("ciphertext.txt","w");
fp_decode_text=fopen("decode_text.txt","w");
fp_key=fopen("key.txt","r");

char c;
init();
int key_cnt=0;
while((c=fgetc(fp_key))!=EOF){
key[key_cnt++]=c;
}
make_sub_key();


int cnt=0;
int sum=0;
while((c=fgetc(fp_plaintext))!=EOF){
plaintext[cnt%8]=c;
cnt++;
sum++;
if(cnt%8==0 && cnt!=0 && sum==8){
des();
for(int i=0;i<8;i++){
fputc(ciphertext[i],fp_ciphertext);
}
decode();//解密
for(int i=0;i<8;i++){
fputc(plaintext[i],fp_decode_text);
}
sum=0;
}
}
if(sum!=0){
for(int i=sum;i<8;i++){
plaintext[i]='0';//不够8位填充0
}
des();
for(int i=0;i<8;i++){
fputc(ciphertext[i],fp_ciphertext);
}
decode();//解密
for(int i=0;i<8;i++){
fputc(plaintext[i],fp_decode_text);
}
}
fclose(fp_plaintext);
fclose(fp_ciphertext);
fclose(fp_decode_text);
fclose(fp_key);
}

//4 实验测试
//笔者将使用上述代码进行实验测试。使用文件读写方式,plaintext.txt文件存放待加密的数据,ciphertext.txt文件存放加密好的数据,key.txt文件存放16位16进制数组成的秘钥,decode_text.txt文件存放经加密再解密的数据。由于本算法实现过程中,加密解密用到的参照表及秘钥完全一样,可以通过对比plaintext.txt文件和decode_text.txt文件内容,如果一致,可以在一定程度上说明算法的正确性。
//(1)plaintext.txt文件中的待加密数据如下:
//I am a student from CCNU.
//I love coding!
//Hello, Wolrd!
//!!!!!!!!!
//(2)key.txt文件中的秘钥如下:
//1234569870abcdef
//(3)程序运行之后,ciphertext.txt文件中的数据如下:
//5ff508a138d8a0f826ae29f6c9b80ad4fefdebf88062bd1574f648693a63c2e5
//(4)程序运行之后,decode_text.txt文件内容如下:
//I am a student from CCNU.
//I love coding!
//Hello, Wolrd!
//!!!!!!!!!
//对比plaintext.txt文件和decode_text.txt文件内容,完全一致,这表明原始数据和加密后再解密的数据一致,可以在一定程度上说明算法的正确性。