65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中 DES(数据加密标准)的简化版本

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (13投票s)

2010 年 7 月 5 日

CPOL

6分钟阅读

viewsIcon

162379

downloadIcon

13273

实现 DES 块密码算法的简化版本——这是 AES 之前的标准加密算法,使用 C# 加密和解密二进制文件。

引言

数据加密标准 (DES) 是一种分组密码(一种对称密钥加密形式),于 1976 年被美国国家标准局选为美国官方的联邦信息处理标准 (FIPS),此后在国际上得到广泛使用。它基于一种使用 56 位密钥的对称密钥算法。该算法最初因有保密的設計元素、相对较短的密钥长度以及对国家安全局 (NSA) 后门的怀疑而备受争议。DES 因此受到严格的学术审查,这促成了对分组密码及其密码分析的现代理解。

DES 目前被认为对许多应用来说是不安全的。这主要是因为 56 位密钥长度太短;1999 年 1 月,distributed.net 和电子前沿基金会合作,在 22 小时 15 分钟内公开破解了一个 DES 密钥。还有一些分析结果表明该密码在理论上存在弱点,尽管在实践中难以利用。该算法以三重 DES (Triple DES) 的形式被认为在实践中是安全的,尽管仍然存在理论攻击的可能性。近年来,该密码已被高级加密标准 (AES) 取代。

此外,DES 已被美国国家标准与技术研究院(前身为美国国家标准局)撤销为标准。[维基百科]

简化 DES 由圣克拉拉大学的 Edward Schaefer 教授开发,是一种用于教学目的的加密算法,而非安全加密算法。它具有与 DES 相似的属性和结构,但参数要小得多。

在本文中,我们将使用 SDES 来加密和解密二进制文件。

背景

图 C.1 描绘了简化 DES(我们称之为 SDES)的整体结构。S-DES 加密算法以 8 位明文块(例如:10111101)和 10 位密钥作为输入,并产生 8 位密文块作为输出。S-DES 解密算法以 8 位密文块和用于生成该密文的相同 10 位密钥作为输入,并产生原始的 8 位明文块。

加密算法包含五个函数:初始置换 (IP);一个复杂的函数 fK,它涉及置换和替换操作,并依赖于密钥输入;一个简单的置换函数 SW,它交换数据的两个半部分;再次使用函数 fK;最后,使用一个与初始置换 (IP-1) 相逆的置换函数。使用多个置换和替换阶段会使算法更加复杂,从而增加密码分析的难度。函数 fK 的输入不仅包括通过加密算法的数据,还包括一个 8 位密钥。该算法可以设计为使用 16 位密钥,由两个 8 位子密钥组成,每个子密钥用于 fK 的一次出现。或者,可以使用单个 8 位密钥,并在算法中两次使用相同的密钥。折衷方案是使用一个 10 位密钥,从中生成两个 8 位子密钥,如图 C.1 所示。在这种情况下,密钥首先经过置换 (P10)。然后执行移位操作。移位操作的输出然后通过一个置换函数,为第一个子密钥 (K1) 产生一个 8 位输出 (P8)。移位操作的输出也馈送到另一个移位操作和另一个 P8 实例,以产生第二个子密钥 (K2)。我们可以将加密算法简洁地表示为函数的组合。

Using the Code

步骤 1:S-DES 密钥生成

S-DES 依赖于发送方和接收方之间共享的 10 位密钥。从该密钥中,会生成两个 8 位子密钥,用于加密和解密算法的特定阶段。上图描述了生成子密钥的阶段。首先,按照以下方式置换密钥。将 10 位密钥指定为 (k1, k2, k3, k4, k5, k6, k7, k8, k9, k10)。然后定义置换 P10 为:P10(k1, k2, k3, k4, k5, k6, k7, k8, k9, k10) = (k3, k5, k2, k7, k4, k10, k1, k9, k8, k6)。

176          //generates  permated array P10
177          BitArray P10(BitArray key)
178          {
179              //0 1 2 3 4 5 6 7 8 9
180              //2 4 1 6 3 9 0 8 7 5
181              BitArray permutatedArray = new BitArray(10);
182  
183              permutatedArray[0] = key[2];
184              permutatedArray[1] = key[4];
185              permutatedArray[2] = key[1];
186              permutatedArray[3] = key[6];
187              permutatedArray[4] = key[3];
188              permutatedArray[5] = key[9];
189              permutatedArray[6] = key[0];
190              permutatedArray[7] = key[8];
191              permutatedArray[8] = key[7];
192              permutatedArray[9] = key[5];
193  
194              return permutatedArray;
195          }

接下来,我们应用 P8,它根据以下方式选择并置换 10 位中的 8 位。

rule:  P8
P8(1 2 3 4 5 6 7 8 9 10) = (6 3 7 4 8 5 10 9 )

然后,我们回到由两个 LS-1 函数生成的 5 位字符串对,并对每个字符串执行 2 位位置的循环左移。

289          BitArray Circular_left_shift(BitArray a, int bitNumber)
290          {
291              BitArray shifted = new BitArray(a.Length);
292              int index = 0;
293              for (int i = bitNumber; index < a.Length; i++)
294              {
295                  shifted[index++] = a[i%a.Length]; 
296              }
297              return shifted;
298          }
299 

步骤 2:S-DES 加密

2.a 初始置换和最终置换

算法的输入是 8 位明文块,我们首先使用 IP 函数对其进行置换:IP(1 2 3 4 5 6 7 8) = (2 6 3 1 4 8 5 7)。

251          //generates permuted text IP
252          BitArray IP(BitArray plainText)
253          {
254              //0 1 2 3 4 5 6 7
255              //1 5 2 0 3 7 4 6
256              BitArray permutatedArray = new BitArray(8);
257  
258              permutatedArray[0] = plainText[1];
259              permutatedArray[1] = plainText[5];
260              permutatedArray[2] = plainText[2];
261              permutatedArray[3] = plainText[0];
262              permutatedArray[4] = plainText[3];
263              permutatedArray[5] = plainText[7];
264              permutatedArray[6] = plainText[4];
265              permutatedArray[7] = plainText[6];
266  
267              return permutatedArray;
268          }

这保留了明文的所有 8 位,但对其进行了混合。在算法结束时,使用逆置换:IP 逆置换(1 2 3 4 5 6 7 8) = (4 1 3 5 7 2 8 6)。

270          BitArray RIP(BitArray permutedText)
271          {
272              //0 1 2 3 4 5 6 7 
273              //3 0 2 4 6 1 7 5
274  
275              BitArray permutatedArray = new BitArray(8);
276  
277              permutatedArray[0] = permutedText[3];
278              permutatedArray[1] = permutedText[0];
279              permutatedArray[2] = permutedText[2];
280              permutatedArray[3] = permutedText[4];
281              permutatedArray[4] = permutedText[6];
282              permutatedArray[5] = permutedText[1];
283              permutatedArray[6] = permutedText[7];
284              permutatedArray[7] = permutedText[5];
285  
286              return permutatedArray;
287          }
2.b 函数 fK

S-DES 最复杂的组件是 fK 函数,它由置换和替换函数的组合组成。函数可以表示如下。令 L 和 R 为 fK 的 8 位输入的左 4 位和右 4 位,令 F 为从 4 位字符串到 4 位字符串的映射(不一定是 一对一)。则令

337          BitArray Fk(BitArray IP, BitArray key)
338          {
339              BitArray[] temp = Split_Block(IP);
340              BitArray Left = Xor(temp[0], F(temp[1], key));
341              BitArray joined = new BitArray(8);
342              int index = 0;
343              for (int i = 0; i < 4; i++)
344              {
345                  joined[index++] = Left[i];
346              }
347              for (int i = 0; i < 4; i++)
348              {
349                  joined[index++] = temp[1][i];
350              }
351              return joined;
352          }

232          BitArray EP(BitArray input)
233          {
234              //0 1 2 3
235              //4 1 2 3 2 3 4 1
236              //3 0 1 2 1 2 3 0
237              BitArray permutatedArray = new BitArray(8);
238  
239              permutatedArray[0] = input[3];
240              permutatedArray[1] = input[0];
241              permutatedArray[2] = input[1];
242              permutatedArray[3] = input[2];
243              permutatedArray[4] = input[1];
244              permutatedArray[5] = input[2];
245              permutatedArray[6] = input[3];
246              permutatedArray[7] = input[0];
247  
248              return permutatedArray;
249          }
2.c S-盒

前 4 位(前面矩阵的第一行)被输入到 S0 S-盒以产生 2 位输出,剩余 4 位(第二行)被输入到 S1 以产生另一个 2 位输出。这两个框定义如下:

S-盒的操作如下。第一个和第四个输入位被视为指定 S-盒行的 2 位数字,第二个和第三个输入位指定 S-盒的列。该行和列中的条目(以二进制表示)是 2 位输出。

318          BitArray S_Boxes(BitArray input, int no)
319          {
320              BitArray[,] current_S_Box;
321  
322              if (no == 1)
323                  current_S_Box = S_Box1;
324              else
325                  current_S_Box = S_Box2;
326  
327              return current_S_Box[binstr2decimal(bin2str(input[0]) + bin2str(input[3])),
328                  binstr2decimal(bin2str(input[1]) + bin2str(input[2]))];
329          }

接下来,S0 和 S1 生成的 4 位将经历进一步的置换,如下所示:P4(1 2 3 4) = (2 4 3 1)。

P4 的输出就是函数 F 的输出。

331          BitArray F(BitArray right, BitArray sk)
332          {
333              BitArray[] temp = Split_Block(Xor(EP(right), sk));
334              return P4(S_Boxes(temp[0], 1), S_Boxes(temp[1], 2));
335          }
2.d 交换函数

fK 函数仅改变输入的左 4 位。交换函数 (SW) 交换左 4 位和右 4 位,以便第二次使用 fK 操作不同的 4 位。在第二次使用中,E/P、S0、S1 和 P4 函数相同。密钥输入是 K2。

354          BitArray Switch(BitArray input)
355          {
356              BitArray switched = new BitArray(8);
357              int index = 0;
358              for (int i = 4; index < input.Length; i++)
359              {
360                  switched[index++] = input[i%input.Length];
361              }
362              return switched;
363          }
2.e 最后,将它们全部放入加密和解密过程
 84          public byte Encrypt(byte block)
 85          {
 86              BitArray bits_block = byte2bits(block);
 87              BitArray[] keys = Generate_Keys();
 88              return bits2byte(RIP(Fk(Switch(Fk(IP(bits_block), keys[0])), keys[1])));
 89              //ciphertext = IP-1( fK2 ( SW (fK1 (IP (plaintext)))))
 90          }
 91  
 92          public byte Decrypt(byte block)
 93          {
 94              BitArray bits_block = byte2bits(block);
 95              BitArray[] keys = Generate_Keys();
 96              return bits2byte(RIP(Fk(Switch(Fk(IP(bits_block), keys[1])), keys[0])));
 97              //IP-1 ( fK1( SW( fK2( IP(ciphertext)))))
 98          }

步骤 3:使用 S-DES 加密和解密二进制文件

3.a 加密过程
116          private void Encrypt()
117          {
118              FileStream fs = new FileStream(txt_enc_in.Text, FileMode.Open);
119              BinaryReader br = new BinaryReader(fs);
120              FileStream fs2 = new FileStream(txt_enc_out.Text, FileMode.Create);
121              BinaryWriter bwr = new BinaryWriter(fs2);
122              int blocksize = 4 * 1024;
123              int iteration_number;
124              if (fs.Length < blocksize)
125                  iteration_number = 1;
126              else if (fs.Length % blocksize == 0)
127                  iteration_number = (int)fs.Length / blocksize;
128              else
129                  iteration_number = ((int)fs.Length / blocksize) + 1;
130              while (iteration_number-- > 0)
131              {
132                  if (iteration_number == 0)
133                      blocksize = (int)fs.Length % blocksize;
134                  byte[] input = br.ReadBytes(blocksize);
135                  byte[] output = new byte[input.Length];
136                  for (int i = 0; i < output.Length; i++)
137                  {
138                      output[i] = my_Des.Encrypt(input[i]);
139                  }
140                  bwr.Write(output);
141                  bwr.Flush();
142              }
143              bwr.Close();
144              fs2.Close();
145              br.Close();
146              fs.Close();
147          }

3.b 解密过程
149          private void Decrypt()
150          {
151              FileStream fs = new FileStream(txt_dec_in.Text, FileMode.Open);
152              BinaryReader br = new BinaryReader(fs);
153              FileStream fs2 = new FileStream(txt_dec_out.Text, FileMode.Create);
154              BinaryWriter bwr = new BinaryWriter(fs2);
155              int blocksize = 4 * 1024;
156              int iteration_number;
157              if (fs.Length < blocksize)
158                  iteration_number = 1;
159              else if (fs.Length % blocksize == 0)
160                  iteration_number = (int)fs.Length / blocksize;
161              else
162                  iteration_number = ((int)fs.Length / blocksize) + 1;
163              while (iteration_number-- > 0)
164              {
165                  if (iteration_number == 0)
166                      blocksize = (int)fs.Length % blocksize;
167                  byte[] input = br.ReadBytes(blocksize);
168                  byte[] output = new byte[input.Length];
169                  for (int i = 0; i < output.Length; i++)
170                  {
171                      output[i] = my_Des.Decrypt(input[i]);
172                  }
173                  bwr.Write(output);
174                  bwr.Flush();
175              }
176              bwr.Close();
177              fs2.Close();
178              br.Close();
179              fs.Close();
180          }

参考文献

  • Cryptography and Network Security, Fourth Edition - William Stallings, Copyright 2006.
© . All rights reserved.