DES算法

2019-04-14    20:40:19


 

一、概要设计

这个程序主要是 DES算法部分的 加密和解密,还有后面对文件的操作。

  程序主要流程图如下:

    

 

                             -1  程序流程图

这个程序的关键在DES算法的操作,主要有下的主要步骤:

1.初始置换 IP

2.子密钥 Ki 的获取;

3.密码函数 f

4.尾置换 IP-1

下面是具体的几个过程:

1) 初始置换IP

这一部分很简单,IPinitial permutation)是一个 8x8 的置换表:

int 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 };  

根据表中的规定,将输入的 64 位明文重新进行排序,即将第 58 位放到第 1 位,第 50 位放到第 2 位……以此类推。初始置换以后得到的是一个 64 位的输出。     

2) 子密钥 Ki 的获取

   用户输出的密钥是 64 位的,根据密钥置换表PC-1,将 64 位变成 56 位密钥。(去掉了奇偶校验位)将 PC-1 置换得到的 56 位密钥,分为前28 C0 和后28 D0,分别对它们进行循环左移,C0左移得到 C1D0 左移得到 D1。将 C1 D1 合并成 56 位,然后通过PC-2表进行压缩置换,得到当前这一轮的 48 位子密钥 K1 。然后对 C1 D1 进行左移和压缩置换,获取下一轮的子密钥……一共进行16轮,得到 16 48 位的子密钥。

 3) 密码函数 f

l  密码函数f(R, K)接受两个输入:32 位的数据和 48 位的子密钥。然后:

l  通过表 E 进行扩展置换,将输入的 32 位数据扩展为 48 位;

l  将扩展后的 48 位数据与 48 位的子密钥进行异或运算;

l  将异或得到的 48 位数据分成 8 6 位的块,每一个块通过对应的一个 S 表产生一个 4 位的输出。

其中,每个 S 表都是 4 16 列。具体的置换过程如下:把 6 位输入中的第 1 位和第 6 位取出来行成一个两位的二进制数 x ,作为 Si 表中的行数(0~3);把 6 位输入的中间 4 位构成另外一个二进制数 y,作为 Si 表的列数(0~15);查出 Si 表中 x y 列所对应的整数,将该整数转换为一个 4 位的二进制数。

把通过 S 表置换得到的 8 4 位连在一起,形成一个 32 位的数据。然后将该 32 位数据通过表 P 进行置换(称为P-置换),置换后得到一个仍然是 32 位的结果数据,这就是f(R, K)函数的输出。

4) 尾置换IP-1

合并 L16 R16 得到一个 64 位的数据,再经过尾置换后得到的就是 64 位的密文。注意:要将 L16 R16 合并成 R16L16(即左右互换)。尾置换表IP-1如下:

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

 

DES算法代码:

  1 #include<iostream>
  2 #include<bitset>
  3 #include <fstream>
  4 #include<cstring>
  5 using namespace std; 
  6 /*
  7 在打开文件时,如果要指定文件名,可以用C风格的字符串。
  8 如果用到string类型的字符串作为文件名时,就必须调用c_str()方法将其转换为一个C风格字符串
  9 */
 10 bitset<64> key;                // 64位密钥  
 11 bitset<48> subKey[16];         // 存放16轮子密钥  
 12 
 13                                // 初始置换表  
 14 int IP[] = { 58, 50, 42, 34, 26, 18, 10, 2,
 15 60, 52, 44, 36, 28, 20, 12, 4,
 16 62, 54, 46, 38, 30, 22, 14, 6,
 17 64, 56, 48, 40, 32, 24, 16, 8,
 18 57, 49, 41, 33, 25, 17, 9,  1,
 19 59, 51, 43, 35, 27, 19, 11, 3,
 20 61, 53, 45, 37, 29, 21, 13, 5,
 21 63, 55, 47, 39, 31, 23, 15, 7 };
 22 
 23 // 结尾置换表  
 24 int IP_1[] = { 40, 8, 48, 16, 56, 24, 64, 32,
 25 39, 7, 47, 15, 55, 23, 63, 31,
 26 38, 6, 46, 14, 54, 22, 62, 30,
 27 37, 5, 45, 13, 53, 21, 61, 29,
 28 36, 4, 44, 12, 52, 20, 60, 28,
 29 35, 3, 43, 11, 51, 19, 59, 27,
 30 34, 2, 42, 10, 50, 18, 58, 26,
 31 33, 1, 41,  9, 49, 17, 57, 25 };
 32 
 33 /*------------------下面是生成密钥所用表-----------------*/
 34 
 35 // 密钥置换表,将64位密钥变成56位  
 36 int PC_1[] = { 57, 49, 41, 33, 25, 17, 9,
 37 1, 58, 50, 42, 34, 26, 18,
 38 10,  2, 59, 51, 43, 35, 27,
 39 19, 11,  3, 60, 52, 44, 36,
 40 63, 55, 47, 39, 31, 23, 15,
 41 7, 62, 54, 46, 38, 30, 22,
 42 14,  6, 61, 53, 45, 37, 29,
 43 21, 13,  5, 28, 20, 12,  4 };
 44 
 45 // 压缩置换,将56位密钥压缩成48位子密钥  
 46 int PC_2[] = { 14, 17, 11, 24,  1,  5,
 47 3, 28, 15,  6, 21, 10,
 48 23, 19, 12,  4, 26,  8,
 49 16,  7, 27, 20, 13,  2,
 50 41, 52, 31, 37, 47, 55,
 51 30, 40, 51, 45, 33, 48,
 52 44, 49, 39, 56, 34, 53,
 53 46, 42, 50, 36, 29, 32 };
 54 
 55 // 每轮左移的位数  
 56 int shiftBits[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
 57 
 58 /*------------------下面是密码函数 f 所用表-----------------*/
 59 
 60 // 扩展置换表,将 32位 扩展至 48位  
 61 int E[] = { 32,  1,  2,  3,  4,  5,
 62 4,  5,  6,  7,  8,  9,
 63 8,  9, 10, 11, 12, 13,
 64 12, 13, 14, 15, 16, 17,
 65 16, 17, 18, 19, 20, 21,
 66 20, 21, 22, 23, 24, 25,
 67 24, 25, 26, 27, 28, 29,
 68 28, 29, 30, 31, 32,  1 };
 69 
 70 // S盒,每个S盒是4x16的置换表,6位 -> 4位  
 71 int S_BOX[8][4][16] = {
 72     {
 73         { 14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7 },
 74         { 0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8 },
 75         { 4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0 },
 76         { 15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13 }
 77     },
 78     {
 79         { 15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10 },
 80         { 3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5 },
 81         { 0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15 },
 82         { 13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9 }
 83     },
 84     {
 85         { 10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8 },
 86         { 13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1 },
 87         { 13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7 },
 88         { 1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12 }
 89     },
 90     {
 91         { 7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15 },
 92         { 13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9 },
 93         { 10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4 },
 94         { 3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14 }
 95     },
 96     {
 97         { 2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9 },
 98         { 14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6 },
 99         { 4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14 },
100         { 11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3 }
101     },
102     {
103         { 12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11 },
104         { 10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8 },
105         { 9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6 },
106         { 4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13 }
107     },
108     {
109         { 4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1 },
110         { 13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6 },
111         { 1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2 },
112         { 6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12 }
113     },
114     {
115         { 13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7 },
116         { 1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2 },
117         { 7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8 },
118         { 2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11 }
119     }
120 };
121 
122 // P置换,32位 -> 32位  
123 int P[] = { 16,  7, 20, 21,
124 29, 12, 28, 17,
125 1, 15, 23, 26,
126 5, 18, 31, 10,
127 2,  8, 24, 14,
128 32, 27,  3,  9,
129 19, 13, 30,  6,
130 22, 11,  4, 25 };
131 
132 /**********************************************************************/
133 /*                                                                    */
134 /*                            下面是DES算法实现                         */
135 /*                                                                    */
136 /**********************************************************************/
137 
138 /**
139 *  密码函数f,接收32位数据和48位子密钥,产生一个32位的输出
140 */
141 bitset<32> f(bitset<32> R, bitset<48> k)
142 {
143     bitset<48> expandR;
144     // 第一步:扩展置换,32 -> 48  
145     for (int i = 0; i<48; ++i)
146         expandR[47 - i] = R[32 - E[i]];
147     // 第二步:异或  
148     expandR = expandR ^ k;
149     // 第三步:查找S_BOX置换表  
150     bitset<32> output;
151     int x = 0;
152     for (int i = 0; i<48; i = i + 6)
153     {
154         int row = expandR[47 - i] * 2 + expandR[47 - i - 5];
155         int col = expandR[47 - i - 1] * 8 + expandR[47 - i - 2] * 4 + expandR[47 - i - 3] * 2 + expandR[47 - i - 4];
156         int num = S_BOX[i / 6][row][col];
157         bitset<4> binary(num);
158         output[31 - x] = binary[3];
159         output[31 - x - 1] = binary[2];
160         output[31 - x - 2] = binary[1];
161         output[31 - x - 3] = binary[0];
162         x += 4;
163     }
164     // 第四步:P-置换,32 -> 32  
165     bitset<32> tmp = output;
166     for (int i = 0; i<32; ++i)
167         output[31 - i] = tmp[32 - P[i]];
168     return output;
169 }
170 
171 /**
172 *  对56位密钥的前后部分进行左移
173 */
174 bitset<28> leftShift(bitset<28> k, int shift)
175 {
176     bitset<28> tmp = k;
177     for (int i = 27; i >= 0; --i)
178     {
179         if (i - shift<0)
180             k[i] = tmp[i - shift + 28];
181         else
182             k[i] = tmp[i - shift];
183     }
184     return k;
185 }
186 
187 /**
188 *  生成16个48位的子密钥
189 */
190 void generateKeys()
191 {
192     bitset<56> realKey;
193     bitset<28> left;
194     bitset<28> right;
195     bitset<48> compressKey;
196     // 去掉奇偶标记位,将64位密钥变成56位  
197     for (int i = 0; i<56; ++i)
198         realKey[55 - i] = key[64 - PC_1[i]];
199     // 生成子密钥,保存在 subKeys[16] 中  
200     for (int round = 0; round<16; ++round)
201     {
202         // 前28位与后28位  
203         for (int i = 28; i<56; ++i)
204             left[i - 28] = realKey[i];
205         for (int i = 0; i<28; ++i)
206             right[i] = realKey[i];
207         // 左移  
208         left = leftShift(left, shiftBits[round]);
209         right = leftShift(right, shiftBits[round]);
210         // 压缩置换,由56位得到48位子密钥  
211         for (int i = 28; i<56; ++i)
212             realKey[i] = left[i - 28];
213         for (int i = 0; i<28; ++i)
214             realKey[i] = right[i];
215         for (int i = 0; i<48; ++i)
216             compressKey[47 - i] = realKey[56 - PC_2[i]];
217         subKey[round] = compressKey;
218     }
219 }
220 
221 /**
222 *  工具函数:将char字符数组转为二进制
223 */
224 bitset<64> charToBitset(const char s[8])
225 {
226     bitset<64> bits;
227     for (int i = 0; i<8; ++i)
228         for (int j = 0; j<8; ++j)
229             bits[i * 8 + j] = ((s[i] >> j) & 1);
230     return bits;
231 }
232 
233 /**
234 *  DES加密
235 */
236 bitset<64> encrypt(bitset<64>& plain)
237 {
238     bitset<64> cipher;
239     bitset<64> currentBits;
240     bitset<32> left;
241     bitset<32> right;
242     bitset<32> newLeft;
243     // 第一步:初始置换IP  
244     for (int i = 0; i<64; ++i)
245         currentBits[63 - i] = plain[64 - IP[i]];
246     // 第二步:获取 Li 和 Ri  
247     for (int i = 32; i<64; ++i)
248         left[i - 32] = currentBits[i];
249     for (int i = 0; i<32; ++i)
250         right[i] = currentBits[i];
251     // 第三步:共16轮迭代  
252     for (int round = 0; round<16; ++round)
253     {
254         newLeft = right;
255         right = left ^ f(right, subKey[round]);
256         left = newLeft;
257     }
258     // 第四步:合并L16和R16,注意合并为 R16L16  
259     for (int i = 0; i<32; ++i)
260         cipher[i] = left[i];
261     for (int i = 32; i<64; ++i)
262         cipher[i] = right[i - 32];
263     // 第五步:结尾置换IP-1  
264     currentBits = cipher;
265     for (int i = 0; i<64; ++i)
266         cipher[63 - i] = currentBits[64 - IP_1[i]];
267     // 返回密文  
268     return cipher;
269 }
270 
271 /**
272 *  DES解密
273 */
274 bitset<64> decrypt(bitset<64>& cipher)
275 {
276     bitset<64> plain;
277     bitset<64> currentBits;
278     bitset<32> left;
279     bitset<32> right;
280     bitset<32> newLeft;
281     // 第一步:初始置换IP  
282     for (int i = 0; i<64; ++i)
283         currentBits[63 - i] = cipher[64 - IP[i]];
284     // 第二步:获取 Li 和 Ri  
285     for (int i = 32; i<64; ++i)
286         left[i - 32] = currentBits[i];
287     for (int i = 0; i<32; ++i)
288         right[i] = currentBits[i];
289     // 第三步:共16轮迭代(子密钥逆序应用)  
290     for (int round = 0; round<16; ++round)
291     {
292         newLeft = right;
293         right = left ^ f(right, subKey[15 - round]);
294         left = newLeft;
295     }
296     // 第四步:合并L16和R16,注意合并为 R16L16  
297     for (int i = 0; i<32; ++i)
298         plain[i] = left[i];
299     for (int i = 32; i<64; ++i)
300         plain[i] = right[i - 32];
301     // 第五步:结尾置换IP-1  
302     currentBits = plain;
303     for (int i = 0; i<64; ++i)
304         plain[63 - i] = currentBits[64 - IP_1[i]];
305     // 返回明文  
306     return plain;
307 }
308 
309 void encryptfile()
310 {
311     string a, b, k;
312 //    char filename[20];
313     cout << "请输入待加密文件名(完整路径):" << endl;
314     cin >> a;
315     cout << "请输入密钥:" << endl;
316     cin >> k;
317     cout << "请输入密文文件名(完整路径):" << endl;
318     cin >> b;
319     key = charToBitset(k.c_str());
320     generateKeys();   // 生成16个子密钥  
321     ifstream in;
322     ofstream out;
323 //    strcpy(filename,a);
324     in.open(a.c_str(), std::ios::binary);  
325 
326     out.open(b.c_str(), ios::binary);
327     bitset<64> plain;
328     while (in.read((char*)&plain, sizeof(plain)))
329     {
330         bitset<64> cipher = encrypt(plain);
331         out.write((char*)&cipher, sizeof(cipher));
332         plain.reset();  // 置0  
333     }
334     in.close();
335     out.close();
336     cout << "加密成功,密文文件在" << b << endl;
337 }
338 
339 void decodefile() 
340 {
341     string a, b, k;
342     cout << "请输入密文文件名(完整路径):" << endl;
343     cin >> a;
344     cout << "请输入密钥:" << endl;
345     cin >> k;
346     cout << "请输入解密后文件名(完整路径):" << endl;
347     cin >> b;
348     key = charToBitset(k.c_str());
349     generateKeys();   // 生成16个子密钥  
350     ifstream in;
351     ofstream out;
352     in.open(a.c_str(), ios::binary);
353     out.open(b.c_str(), ios::binary);
354     bitset<64> plain;
355     while (in.read((char*)&plain, sizeof(plain)))
356     {
357         bitset<64> temp = decrypt(plain);
358         out.write((char*)&temp, sizeof(temp));
359         plain.reset();  // 置0  
360     }
361     in.close();
362     out.close();
363     cout << "解密成功,明文文件在" << b << endl;
364 }
365 int main(){    
366     int cycle=0;
367     while(cycle!=(-1)){
368         cout<<"****************DES加密解密文件工具****************"<<endl;
369         cout<<"****************1、加密文件****************"<<endl;
370         cout<<"****************2、解密文件****************"<<endl;
371         cout<<"****************0、退出********************"<<endl;
372         cout<<"请选择功能(1-2):"<<endl;
373     
374         
375             int n;
376             cin>>n;
377             switch(n){
378                 case 1:
379                     encryptfile();
380                     printf("\n\nPress Enter Contiue!\n");
381                     getchar();
382                     while(getchar()!='\n');
383                     break;
384                     
385                 case 2:
386                     decodefile();
387                     printf("\n\nPress Enter Contiue!\n");
388                     getchar();
389                     while(getchar()!='\n');
390                     break;
391                     
392                 case 0:
393                     cycle=(-1);
394                     
395                     break;
396                 default:
397                  
398                     cout<<"你的输入有误!"<<endl;
399                     cout<<"\nPress Enter Contiue!\n";
400                     getchar();
401                  
402                     while(getchar()!='\n');
403                     break;
404             
405             }
406             
407     }
408 }