Python实现DES加解密功能(附代码示例)

1. 子密钥生成和初始置换、逆初始置换

1.1 初始置换部分原理自述

DES 算法使用 64 位的密钥key 将 64 位的明文变为 64 位的密文输出, 并把输出块分为左Left、右 Right 两部分,每部分均为 32 位。初始置换规则如下:

表 1-1 初始置换 IP

即将输入的 64 位明文的第 1 位置换到第 40 位,第 2 位置换到第 8 位,

第 3 位置换到第 48 位。以此类推,最后一位是原来的第 7 位。置换规则是规定的。Left 是置换后的数据的前 32 位,Right 是置换后的数据的后 32 位。

初始置换代码:

1.2 子密钥生成部分原理自述:

DES 算法由 64 位秘钥产生 16 轮的 48 位子秘钥。在每一轮的迭代过程中,使用不同的子秘钥。

  1. 把密钥的奇偶校验位忽略不参与计算(即每个字节的第 8 位),将 64 位密钥减至 56 位,然后根据PC1 置换将这 56 位分成左右两部分C0(28 位)和D0(28 位);
  2. 将 C0 和D0 进行循环左移变化(每轮循环左移的位数由轮数决定),变换后生成 C1 和D1,然后 C1 和D1 合并,并通过 PC-2 置换生成子密钥K1(48 位);
  3. 和D1 再次经过循环左移变换,生成C2 和D2,然后C2 和D2 合并,通过PC-2 置换生成密钥K2(48 位);
  4. 以此类推,得到 K16(48 位)。但是最后一轮的左右两部分不交

换,而是直接合并在一起,作为逆置换的输入。其中循环左移的位数一共是循环左移 16 次,其中第 1 次、第 2 次、第 9 次、第 16 次是循环左移一位,其他都是左移两位。

1.3 PC-1 置换部分原理自述:

该步骤操作对象是 64 位密钥,将 64 位秘钥降至 56 位秘钥, 通过PC1 置换的变换变成 56 位。如下:

                                                                         表 1-2 PC1 置换

再将 56 位秘钥分成C0 和D0:

C0(28 位)=K57K49K41…K44K36 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

D0(28 位)=K63K55K47…K12K4 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

PC-1 置换代码:

1.4 循环左移原理自述:

循环左移每轮移动位数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

1

1

2

2

2

2

2

2

2

1

2

2

2

2

2

2

1

表 1-3 左移位次

第一轮是循环左移 1 位。C0 循环左移 1 位后得到C1 如下:

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,57

D0 循环左移 1 位后得到D1 如下:

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,63

循环左移代码:

C1 和D1 合并之后,再经过 PC2 置换表生成 48 位的子秘钥K1。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

表 1-4 PC2 置换

去掉第 9、18、22、25、35、38、43、54 位,从 56 位变成 48 位,再按表的位置置换。

C1 和D1 再次经过循环左移变换,生成C2 和D2,C2 和D2 合并, 通过PC-2 生成子秘钥K2。

以此类推,得到子秘钥K1~K16。需要注意其中循环左移的位数。PC-2 置换代码:

1.5 逆初始置换

原理自述:

将初始置换进行 16 次的迭代,即进行 16 层的加密变换,这个运算过程我们暂时称为函数f。得到L16 和R16,将此作为输入块,进行逆置换得到最终的密文输出块。逆置换是初始置换的逆运算。从初始置换规则中可以看到,原始数据的第 1 位置换到了第 40 位,第 2 位置

换到了第 8 位。则逆置换就是将第 40 位置换到第 1 位,第 8 位置换

到第 2 位。以此类推,逆置换规则如下:

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

表 1-5 逆初始置换

逆初始置换代码:

2. 轮函数

2.1 拓展置换

def _block_extend(block: str) -> str:

"""

选择扩展运算 E """

extended_block = "" extend_table = ( 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

)

for i in extend_table:

extended_block += block[i – 1] print(f"扩展运算{extended_block}") return extended_block

原理自述:拓展置换的目的是将一个 32 位的串根据拓展置换表 E 转换为 48 位,其实就是重复其中的某些位,达到混淆的目的。具体就是将 32 位的数据分成 4*8 小块,每个小块拓展为 6 位。

将右边 32 位拓展置换为 48 位

A = (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)

通过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)

拓展置换结果:

扩展运算 101000000101011011111001010100000111111011111010

扩展运算 100011110110101110101001011101010001011000000010

扩展运算 000011110010101111110001011101010111111001010000

扩展运算 101011110110101101010110100101011100000011111110

扩展运算 011001010010101100000011111100000010100111110001

扩展运算 000010101000001110100101011110101011111110100000

扩展运算 011110101000001100001111110000001010101000001001

扩展运算 011001010001010001011100000001010011110111111001

扩展运算 001000001110100101010011110001011111111010100100

扩展运算 000100000000000100000001011011110100001101011100

扩展运算 001000001010100000001001010010101110101011111000

扩展运算 010110100111111101010010100001010001011110100001

扩展运算 011000000101011100001111111010101000001110101101

扩展运算 111110101101010110100011110100001010100101010011

扩展运算 101010101000000010100010100010100110100111111010

扩展运算 100000000001011111111111110100000000000010101010

2.2 s 盒置换

extended_block 与密钥 keyi 做异或

def _not_or(a: str, b: str) -> str: """

对两个 01 字符串做异或

"""

result = ""

size = len(a) if len(a) < len(a) else len(b)

for i in range(size):

result += '0' if a[i] == b[i] else '1' print(f"异或{result}")

return result

原理自述:

将拓展置换得到的字符串与置换选择后的密钥做异或得到result 异或操作是对相同位置的字符相比较,相同为 0,不同为 1.

A = (0101010101010101)

B = (1010101010101010)

A 与B 进行异或置换得到C = (1111111111111111)

s 盒与密钥keyi 异或结果:

异或 111100010111101001110101111101110011110100111010

异或 100101100100011100100101110100000001010100110101

异或 000101000000001101111100110001110100011111000010

异或 101100000110001011011111101101101010101011010011

异或 011110100110101010011010001010000101100101000000

异或 000001011100001001111100011000111010000100101100

异或 010101011101001001011110000100000110110100110101

异或 010000110100011100001111110011101010011110111001

异或 100001101011101000000001001000001010000011110100

异或 101101001101001101110011111000111110101001100110

异或 110000000011101001111011001010001011011110011010

异或 101110101110100101110100101110111110011110001000

异或 100000001111000100101001101000101011010001100110

异或 001010100111100110000101001001100010110111011111

异或 111110100010110000000110110110101100101010111101

异或 110100000011101101010011100001110010101001101000

2.3 S 盒压缩

原理自述:1. 经过拓展置换后得到的 48 位串与 48 位密钥做异或,得到 48 位密文串,这个串必须压缩成 32 位才能作为下一轮的 left。把这个 48 位的串分成 8 组,每组六位,压缩就是要把 6 位变成 4 位, 这里用到了 8 张 4*16 的S 盒压缩表.

2. 经过拓展置换后得到的 48 位串与 48 位密钥做异或,得到 48 位密文串,每 6 个分一组,分 8 组,如第二组是 111011 就查找把第一位与最后一位取出得到 11,转换为十进制 3 作为行号,中间四位 1101转换为十进制 13 作为列号,查找 s_box2 的 3 行 13 列得到 9,将 9转换为二进制为 1001 就是这 6 为密文压缩后的结果,其他一样,最终会输出 32 位密文串。若第二组是 111011 得到行 11,列 1101则从第二个S 盒表第 3 行,第 13 列得到 9,二进制表示为 1001

def _s_box_replace(self, block48: str) -> str: """

S 盒置换,将 48 位的输入转换为 32 位输出

"""

s_box_table = ( (

(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),

), (

(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),

),

(

(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),

),

(

(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),

), (

(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),

),

(

(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),

),

(

(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),

), (

(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),

)

)

result = ""

for i in range(8):

row_bit = (block48[i * 6] + block48[i * 6 + 5]).encode("utf-8") line_bit = (block48[i * 6 + 1: i * 6 + 5]).encode("utf-8")

row = int(row_bit, 2) line = int(line_bit, 2)

#print(f"第{row}行, 第{line}列") data = s_box_table[i][row][line] no_full = str(bin(data))[2:] while len(no_full) < 4:

no_full = '0' + no_full result += no_full print(f"s 盒 {result}") return result

def _s_box_compression(self, num: int, block48: str) -> str: result_not_or = self._not_or(block48, self.child_keys[num]) print(f"与 key 做异或后的结果{result_not_or}")

return self._s_box_replace(result_not_or)

一组异或过程与结果:

s 盒 1001

s 盒 10011101

s 盒 100111011000

s 盒 1001110110000111

s 盒 10011101100001111011

s 盒 100111011000011110110000

s 盒 1001110110000111101100000001

s 盒 10011101100001111011000000011001

与 key 做异或后的结果 111110100010110000000110110110101100101010111101

2.4 p 盒置换

def _p_box_replacement(self, block32: str) -> str:

"""

P 盒置换

Return:

返回经过 P 盒置换后的 32 位 01 串

"""

p_box_replace_table = (

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,

)

print(f"p 盒{p_box_replace_table}")

return self._replace_block(block32, p_box_replace_table)

原理自述:用 P 盒中字符替换 S 盒压缩后得到的字符串的对应位置字

符。

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)

S 盒压缩后 =(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)

经过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)

2.5 轮函数

def _iteration(self, block: str, key: str, is_decode: bool) -> str: self._key_selection_replacement(key)

"""

16 轮

"""

for i in range(16):

# 轮函数

print("第"+str(i+1)+"轮"+block)

left, right = block[0: 32], block[32: 64] next_left = right

f_result = self._f_function(right, is_decode, i) right = self._not_or(left, f_result)

block = next_left + right print(f"miwen{block[32:] + block[:32]}") return block[32:] + block[:32]

原理自述:首先将明文二进制串分为左右两半:

left, right = block[0: 32], block[32: 64]

右边作为最后左边: next_left = right

接下来右边进行f 函数后与左边进行异或得到最后右边:

f_result = self._f_function(right, is_decode, i)

最后得到右和最后左:block = next_left + right

得到 64 位未逆初始置换的二进制串:

return block[32:] + block[:32]

再进行下一次循环,直至 16 轮循环完成。最后得到:

11101011010010110111010011001111010000101101110010100011110

11101

3. 加解密

3.1 加密部分步骤:

1. 初始置换:将输入的明文块进行初始置换,按照特定的顺序重新排列,以准备进行加密运算。

2. 迭代加密:使用密钥对初始置换后的明文块进行多轮迭代加密,每轮加密都会对明文进行混淆和替换,密钥也会参与运算以增加加密的随机性和强度。

3. 结尾置换:在加密的最后一轮迭代完成后,对加密结果进行结尾置换,以得到最终的加密结果。

4. 二进制转换为十六进制:将加密后的二进制数据转换为十六进制, 并将结果拼接到最终的加密结果字符串中。

加密部分原理自述:

将整个加密函数部分分割为 3 份:

1. 输入处理:

我们使用 self._processing_encode_input(enter)函数实现明文转化为二进制并将其分块处理的操作

2. 加密:

self._init_replace_block(block)函数实现初始置换。它按照预定义的规则重新排列输入数据,以准备进行加密运算。self._iteration 函数实现迭代加密过程。它对初始置换后的数据块进行多轮迭代的加密,每轮加密都会使用密钥对数据进行混淆和替换,增加了加密的强度和随机性。self._end_replace_block(block_result)函数实现逆初始置换。它对加密结果进行逆向的置换操作,以得到最终的加密结果。

3. 输出结果:

result += str(hex(int(block_result.encode(), 2)))函数实现在每个数据块加密完成后,将加密结果转换为十六进制,并拼接到结果字符串中。最后,返回整个加密后的结果字符串。

加密函数代码如下:

加密部分运行结果:

DES 加密后的数据为:0x5bd92773264cf76b

3.2 ​​​​​​​解密部分步骤:

  1. 初始置换:与加密部分相同,对密文块进行初始置换,以准备进行解密运算。
  2. 迭代解密:使用相同的密钥对初始置换后的密文块进行多轮迭代解密,与加密过程相反,每轮解密都会对密文进行逆向的混淆和替换。
  3. 结尾置换:在解密的最后一轮迭代完成后,对解密结果进行结尾置换,以得到最终的解密结果。
  4. 二进制解码:将解密后的二进制数据按照特定的编码规则进行解码,得到原始的明文数据。

​​​​​​​​​​​​​​解密部分原理自述:

将整个解密函数部分分割为 3 份:

1. 输入处理:

self._processing_decode_input(cipher_text):这个函数用于将输入的密文进行预处理,将其分块处理。它将密文字符串拆分成固定长度的块,以便后续解密操作。

2. 解密过程:

self._init_replace_block(block):这个函数实现了 DES 算法中的初始置换。它按照预定义的规则重新排列输入数据,以准备进行解密运算。

self._iteration(irb_result,key,is_decode=True):这个函数实现了DES 算法的迭代解密过程。它对初始置换后的数据块进行多轮迭代的解密,每轮解密都会使用密钥对数据进行逆向的混淆和替换,以还原原始数据。

self._end_replace_block(block_result):这个函数实现了 DES 算法中的逆初始置换。它对解密结果进行逆向的置换操作,以得到最终的解密结果。

3. 结果输出:

在解密后, 每个数据块都是以二进制字符串的形式存储在block_result 中。

For i in range(0,len(block_result),8)::这个循环是将二进制字符串每 8 位分割为一个字节,以便后续解码result.append(block_result[i: i+8]):将每个字节添加到结果列表中。

return self._bit_decode(result):最终将解密后的结果列表传递给_bit_decode 函数进行最终的解码,并返回解密后的结果。

​​​​​​​解密函数代码如下:

return self._bit_decde(result) # 返回解密后的结果

​​​​​​​解密部分运行结果:

解密出的数据为:security

代码:


from bitarray import bitarray
class MyDES:
    def __init__(self):
        self.child_keys = []

    @staticmethod
    def _bit_encode(s: str) -> str:
        """
        将字符串转换为01字符串的形式
        """
        return bitarray(
            ''.join([bin(int('1' + hex(c)[2:], 16))[3:]
                     for c in s.encode('utf-8')])).to01()

    def _str_to__fixed_len_bit(self, s: str, length: int) -> str:
        """
        将字符串转变为固定长度的01字符串
        :param s: 要转换的字符串
        :param length: 目标长度
        :return: 长度为length的01字符串
        """
        bit_iv = self._bit_encode(s)
        while len(bit_iv) < length:
            bit_iv += '0'
        return bit_iv[: length]


    @staticmethod
    def _bit_decode(s: list):
        return ''.join([chr(i) for i in [int(b, 2) for b in s]])

    @staticmethod
    def _negate(s: str):
        result = ""
        try:
            for i in s:
                result += '0' if i == '1' else '1'
            return result
        except:
            print("密钥错误")
            raise

    @staticmethod
    def _replace_block(block: str, replace_table: tuple) -> str:
        """
        对单个块进行置换
        Args:
            block: str, 要进行转换的64位长的01字符串
            replace_table: 转换表
        Return:
            返回转换后的字符串
        """
        result = ""
        for i in replace_table:
            try:
                result += block[i - 1]
            except IndexError:
                print(i)
                # print(f"block= {block}, len={len(block)}")
                raise
        return result

    def _processing_encode_input(self, enter: str) -> list:
        """
        将输入的字符串转换为二进制形式,并没64位为一组进行分割
        """
        result = []
        bit_string = self._bit_encode(enter)
        # 如果长度不能被64整除,就补零
        if len(bit_string) % 64 != 0:
            for i in range(64 - len(bit_string) % 64):
                bit_string += '0'
        for i in range(len(bit_string) // 64):
            result.append(bit_string[i * 64: i * 64 + 64])
        # print(f"转换为二进制后的初始明文: {result}")
        return result

    @staticmethod
    def _processing_decode_input(enter: str) -> list:
        result = []
        try:
            input_list = enter.split("0x")[1:]
            int_list = [int("0x" + i, 16) for i in input_list]
            for i in int_list:
                bin_data = str(bin(i))[2:]
                while len(bin_data) < 64:
                    bin_data = '0' + bin_data
                result.append(bin_data)
            return result
        except Exception as e:
            raise

    def _key_conversion(self, key: str):
        """
        将64位原始密钥转换为56位的密钥,并进行一次置换即DES密钥编排中的置换选择1 PC-1
        """
        key = self._bit_encode(key)
        while len(key) < 64:
            key += '0'
        first_key = key[:64]
        key_replace_table = (
            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
        )
        return self._replace_block(first_key, key_replace_table)

    def _spin_key(self, key: str):
        """
        旋转获得子密钥
        """
        kc = self._key_conversion(key)
        first, second = kc[0: 28], kc[28: 56]
        spin_table = (1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28)
        for i in range(1, 17):
            first_after_spin = first[spin_table[i - 1]:] + first[:spin_table[i - 1]]
            second_after_spin = second[spin_table[i - 1]:] + second[:spin_table[i - 1]]
            print(f"第{i}轮移位:{first_after_spin}{second_after_spin}")
            yield first_after_spin + second_after_spin

    def _key_selection_replacement(self, key: str):
        """
        DES密钥编排中的置换选择2 PC-2
        """
        # 先置空
        self.child_keys = []
        key_select_table = (
            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
        )
        for child_key56 in self._spin_key(key):
            self.child_keys.append(self._replace_block(child_key56, key_select_table))

    def _init_replace_block(self, block: str):
        """
        初始置换
        """
        replace_table = (
            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
        )
        return self._replace_block(block, replace_table)

    def _end_replace_block(self, block: str) -> str:
        """
        逆初始置换
        """
        replace_table = (
            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
        )
        return self._replace_block(block, replace_table)

    @staticmethod
    def _block_extend(block: str) -> str:
        """
        选择扩展运算E
        """
        extended_block = ""
        extend_table = (
            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
        )
        for i in extend_table:
            extended_block += block[i - 1]
        return extended_block

    @staticmethod
    def _not_or(a: str, b: str) -> str:
        """
        对两个01字符串做异或
        """
        result = ""
        size = len(a) if len(a) < len(a) else len(b)
        for i in range(size):
            result += '0' if a[i] == b[i] else '1'
        return result

    def _s_box_replace(self, block48: str) -> str:
        """
        S盒置换,将48位的输入转换为32位输出
        """
        s_box_table = (
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            ),
            (
                (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),
            )
        )
        result = ""
        for i in range(8):
            row_bit = (block48[i * 6] + block48[i * 6 + 5]).encode("utf-8")
            line_bit = (block48[i * 6 + 1: i * 6 + 5]).encode("utf-8")
            row = int(row_bit, 2)
            line = int(line_bit, 2)
            #print(f"第{row}行, 第{line}列")
            data = s_box_table[i][row][line]
            no_full = str(bin(data))[2:]
            while len(no_full) < 4:
                no_full = '0' + no_full
            result += no_full
        return result

    def _s_box_compression(self, num: int, block48: str) -> str:
        """
        对经过拓展置换后的48位01串进行S盒压缩,有两部:
          1. 与key做异或
          2. 根据S盒压缩表经48位压缩为36位
        Args:
            num: 第几次迭代
            block48: right
        Return:
            返回经过S盒压缩后的32位01字符串
        """
        result_not_or = self._not_or(block48, self.child_keys[num])
        #print(f"与key 做异或后的结果{result_not_or}")
        return self._s_box_replace(result_not_or)

    def _p_box_replacement(self, block32: str) -> str:
        """
        P盒置换
        Return:
            返回经过P盒置换后的32位01串
        """
        p_box_replace_table = (
            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,
        )
        return self._replace_block(block32, p_box_replace_table)

    def _f_function(self, right: str, is_decode: bool, num: int):
        right = self._block_extend(right)
        if is_decode:
            sbc_result = self._s_box_compression(15 - num, right)
        else:
            sbc_result = self._s_box_compression(num, right)
        #print(f"s盒压缩后的结果:{sbc_result}")
        return self._p_box_replacement(sbc_result)

    def _iteration(self, block: str, key: str, is_decode: bool) -> str:
        self._key_selection_replacement(key)
        """
        16轮
        """
        for i in range(16):
            # 轮函数
            print("第"+str(i+1)+"轮"+block)

            left, right = block[0: 32], block[32: 64]
            next_left = right
            f_result = self._f_function(right, is_decode, i)
            right = self._not_or(left, f_result)
            block = next_left + right
        return block[32:] + block[:32]

    def encode(self, enter: str, key: str):
        result = ""
        #将输入转化为二进制
        blocks = self._processing_encode_input(enter)
        for block in blocks:
            irb_result = self._init_replace_block(block)
            #初始置换
            print("初始置换:"+ irb_result)
            block_result = self._iteration(irb_result, key, is_decode=False)
            block_result = self._end_replace_block(block_result)
            # 逆初始置换
            print("逆初始置换:"+block_result)
            result += str(hex(int(block_result.encode(), 2)))
        return result

    def decode(self, cipher_text: str, key: str):
        result = []
        blocks = self._processing_decode_input(cipher_text)
        for block in blocks:
            irb_result = self._init_replace_block(block)
            block_result = self._iteration(irb_result, key, is_decode=True)
            block_result = self._end_replace_block(block_result)
            for i in range(0, len(block_result), 8):
                result.append(block_result[i: i+8])
        return self._bit_decode(result)
if __name__ == '__main__':
    key = "12345678"
    #iv = "this is iv"
    md = MyDES()
    des_encode = md.encode("security", key)
    print("DES加密后的数据为:" + des_encode)
    print(f"解密出的数据为:" + md.decode(des_encode, key))

作者:廉价劳动力xx

物联沃分享整理
物联沃-IOTWORD物联网 » Python实现DES加解密功能(附代码示例)

发表评论