DES的原理及python实现

DES加密算法原理及实现

DES是一种对称加密算法【即发送者与接收者持有相同的密钥】,它的基本原理是将要加密的数据划分为n个64位的块,然后使用一个56位的密钥逐个加密每一个64位的块,得到n个64位的密文块,最后将密文块拼接起来得到最终的密文

加密

加密过程

DES加密过程接收一个明文盒一个64位的密钥key,明文字符串会被转换为对各64位的块,加密过程以块位单位,经过初态转换,16轮循环加密,终态转换,最终每个64位的块都会被加密成一个64位的密文块,将得到的密文块拼起来,得到的就是最终加密后的结果。

在这里插入图片描述

    def encode(self, enter: str, key: str):result = ""# 将输入的字符串处理为长度为64的块blocks = self.processing_encode_input(enter)for block in blocks:# 对每一块进行初态置换irb_result = self._init_replace_block(block)# 对每一块进行16轮循环加密block_result = self.iteration(irb_result, key, is_decode=False)# 对每一块进行终态置换block_result = self._end_replace_block(block_result)# 返回16进制形式的密文串result += str(hex(int(block_result.encode(), 2)))return result

1. 明文数据处理processing_encode_input

首先需要将字符串的明文转换为二进制,按64位一组,分成若干组,如果不够64位,就补零。

    from bitarray import bitarray@staticmethoddef _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 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

2. 初态转换

初态转换是将初始的64位块按照 【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

IP 置换表中每一位的意思是:将原来第i位的元素填充到这,如第一个58就表示把原来比特串中第58位放在新串的第1位,第二个50表示把原来比特串的第50位放在新串的第2位…

    @staticmethoddef 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)}")raisereturn resultdef _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)

3. 16轮循环加密iteration

在这里插入图片描述

每一轮循环加密的过程为:

  1. 将初态置换后或上一次循环后得到的64位块分成左右各32位的子块Left盒Right
  2. Right经过f函数转换后得到一个32位的串,这个串与Left做异或后得到下一轮循环的Right
  3. 将这一轮原视的Right作为下一轮的Left
  4. 拼接Left盒Right,进行下一轮循环
    def iteration(self, block: str, key: str) -> str:for i in range(16):# 分成左右两个子块left, right = block[0: 32], block[32: 64]# 将这一轮原视的Right作为下一轮的Leftnext_left = right# f函数f_result = self._f_function(right, i)# f函数的输出与left做异或得到下一轮的rightright = self._not_or(left, f_result)# 拼接,准备进行下一轮block = next_left + rightreturn block[32:] + block[:32]
  • 在最后一轮循环加密之后,left盒right是相反的,所以进行终态置换之前要换过来

3.1 f函数

在这里插入图片描述

f函数有三部分组成:

  1. 拓展置换
  2. S盒置换
  3. P盒置换
    def _f_function(self, right: str, num: int):# 拓展置换right = self.block_extend(right)# S盒置换sbc_result = self.s_box_compression(num, right)# P盒置换return self.p_box_replacement(sbc_result)
3.1.1 拓展置换

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

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

拓展置换表中,每一行代表拓展后的一个小块,内部数字表示原来子块中01的位置,其实就是在每一个小块前面加上前一个小块的最后一个字符,后面加上下一个小块的第一个字符,比如有三个小块:

0 1 0 0     1 0 1 1     1 0 0 1

那么第二个小块拓展之后就是

0 1 0 1 1 1
    @staticmethoddef block_extend(block: str) -> str:"""拓展置换"""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
3.1.2 S盒置换

在这里插入图片描述

S盒置换有两步:

  1. 与密钥keyi{key}_ikeyi做异或
  2. S盒压缩
    def s_box_compression(self, num: int, block48: str) -> str:"""对经过拓展置换后的48位01串进行S盒压缩Args:num: 第几次迭代block48: rightReturn:返回经过S盒压缩后的32位01字符串"""# 与密钥key做异或result_not_or = self._not_or(block48, self.child_keys[num])# S盒压缩return self._s_box_replace(result_not_or)
密钥keyi{key}_ikeyi的获得

在这里插入图片描述

DES的原始密钥是一个64位的01串,其中8,16, 24, 32, 40, 48,56, 64位作为奇偶检验位,通过密钥转换去除,实际加密中使用的只有56位,这56位的密钥经过密钥旋转置换选择会产生16个48位的子密钥,所以每次循环加密用到的子密钥都是不同的。

密钥转换的目的是将64位原始密钥转换为56位的密钥,并进行一次置换

  • 依照的表是密钥转换表

    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
    
  • 代码实现:

        def key_conversion(self, key):"""将64位原始密钥转换为56位的密钥,并进行一次置换"""first_key = keykey_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)
    

密钥旋转:是为了保证16个子密钥各不相同。具体做法是将56位密钥分成两个28位的子串,然后将这两个子串进行循环旋转,具体规则依照DesRotations

Round12345678910111213141516
Rotations1122222212222221
  • Round表示第几轮旋转,也就是第几个key
  • Rotations表示旋转次数
  • 除了第1, 2, 9, 16个key旋转1位,其他都旋转两位

  • 每次旋转是相对于上一次的结果而言的,比如第三个子密钥就是原视的两个28位串向左旋转4位得到的

  • 代码实现

        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"旋转后的key: left: {first_after_spin}, right: {second_after_spin}")yield first_after_spin + second_after_spin
    

置换选择:密钥旋转之后拼接得到一个56位的串,置换选择可以选择其中48位作为最终的keyi{key}_ikeyi,置换选择依照置换选择表

  • 置换选择表

    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  
    
  • 代码实现

        def key_selection_replacement(self, key: str):"""通过选择置换得到48位的子密钥"""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))
    
S盒压缩

密钥keyi{key}_ikeyi与拓展置换后的right做异或后得到一个48位的串,这个串必须压缩成32位才能作为下一轮的left。把这个48位的串分成8组,每组六位,压缩就是要把6位变成4位,这里用到了8张 4*16 的【S盒压缩表】

s_box1 = [[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],
],
s_box2 = [[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],
],
s_box3 = [[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],
],
s_box4 = [[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],
],
s_box5 = [[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],
],
s_box6 = [[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],
],
s_box7 = [[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],
],
s_box8 = [[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],
]

具体做法是:

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

代码实现

    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_fullresult += no_fullreturn result
2.1.3 P盒置换

p盒置换作用也是为了混淆,用到了【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,)return self.replace_block(block32, p_box_replace_table)

4. 终态置换

与初态置换一样, 只不过使用的置换表不一样而已

  • 终态置换表
 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
  • 代码实现
    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)

解密

解密使用与加密相同的算法,只不过使用子密钥的顺序不同而已,加密过程第一轮循环使用key1key_1key1,解密过程第一轮循环使用key16key_{16}key16,可以在循环加密处添加一个标志位完成

分组模式

明文可能被转换为多个64位的块,如果将每个块单独加密,后将所有块加密后的结果简单拼接起来构成最终密文,这样的模式叫做ECB(电码本模式),这种模式简单, 但安全性不高,如果将上一个块的加密后的结果与这个块的原视数据做异或后再加密的模式叫做CBC(分组链接模式),还有CFBOFB

  • CBC模式下DES的加解密流程
    在这里插入图片描述

完整代码

# __author: Junebao
# data:2020/3/9from bitarray import bitarrayclass MyDES:def __init__(self):self.child_keys = []@staticmethoddef _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]@staticmethoddef _bit_decode(s: list):return ''.join([chr(i) for i in [int(b, 2) for b in s]])@staticmethoddef _negate(s: str):result = ""try:for i in s:result += '0' if i == '1' else '1'return resultexcept:print("密钥错误")raise@staticmethoddef _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)}")raisereturn resultdef _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@staticmethoddef _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_dataresult.append(bin_data)return resultexcept Exception as e:raisedef _key_conversion(self, key: str):"""将64位原始密钥转换为56位的密钥,并进行一次置换"""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"旋转后的key: left: {first_after_spin}, right: {second_after_spin}")yield first_after_spin + second_after_spindef _key_selection_replacement(self, key: str):"""通过选择置换得到48位的子密钥"""# 先置空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)@staticmethoddef _block_extend(block: str) -> str:"""拓展置换"""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@staticmethoddef _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 resultdef _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_fullresult += no_fullreturn resultdef _s_box_compression(self, num: int, block48: str) -> str:"""对经过拓展置换后的48位01串进行S盒压缩,有两部:1. 与key做异或2. 根据S盒压缩表经48位压缩为36位Args:num: 第几次迭代block48: rightReturn:返回经过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)for i in range(16):left, right = block[0: 32], block[32: 64]next_left = rightf_result = self._f_function(right, is_decode, i)right = self._not_or(left, f_result)block = next_left + rightreturn 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)block_result = self._iteration(irb_result, key, is_decode=False)block_result = self._end_replace_block(block_result)result += str(hex(int(block_result.encode(), 2)))return resultdef encode_by_cbc(self, enter: str, des_key: str, iv: str):"""使用 CBC 模式进行 DES加密:param enter: 明文:param des_key: 密钥:param iv: CBC模式中的因子:return: 加密后的十六进制格式密文串"""bit_iv = self._str_to__fixed_len_bit(iv, 64)result = ""blocks = self._processing_encode_input(enter)previous = bit_ivfor block in blocks:block = self._not_or(block, previous)irb_result = self._init_replace_block(block)block_result = self._iteration(irb_result, des_key, is_decode=False)block_result = self._end_replace_block(block_result)previous = block_resultresult += str(hex(int(block_result.encode(), 2)))return resultdef 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)def decode_by_cbc(self, cipher_text: str, des_key: str, iv: str):bit_iv = self._str_to__fixed_len_bit(iv, 64)result = []blocks = self._processing_decode_input(cipher_text)previous = bit_ivfor block in blocks:irb_result = self._init_replace_block(block)block_result = self._iteration(irb_result, des_key, is_decode=True)block_result = self._end_replace_block(block_result)block_result = self._not_or(block_result, previous)previous = blockfor i in range(0, len(block_result), 8):result.append(block_result[i: i+8])return self._bit_decode(result)if __name__ == '__main__':key = "hahahha"iv = "this is iv"md = MyDES()des_encode = md.encode("junebao.top", key)print("DES加密后的数据为:" + des_encode)print(f"解密出的数据为:" + md.decode(des_encode, key))cbc_encode = md.encode_by_cbc('http://blog.junebao.top', des_key=key, iv=iv)print(f"CBC mode encryption: {cbc_encode}")print(f"CBC mode decrypt: {md.decode_by_cbc(cbc_encode, des_key=key, iv=iv)}")

结果:

在这里插入图片描述

参考

https://blog.csdn.net/wowocpp/article/details/80132097
https://www.bilibili.com/video/av75630674?t=133&p=7
https://blog.csdn.net/weixin_42940826/article/details/83687007?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457490.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为手机充满有提醒吗_2020手机充电速度排名:最快21分钟充满,华为第15名

5G手机扎堆出现&#xff0c;中国5G基站数量也是不断增多&#xff0c;中国移动曾经表态&#xff0c;2020年底将会在全国地级市覆盖5G网络&#xff0c;全民5G时代终于到来&#xff01;从目前国内手机出货量数据来看&#xff0c;5G手机占比已经达到了六成以上&#xff0c;国产5G手…

关于移动手机端富文本编辑器qeditor图片上传改造

日前项目需要在移动端增加富文本编辑&#xff0c;上网找了下&#xff0c;大多数都是针对pc版的&#xff0c;不太兼容手机&#xff0c;当然由于手机屏幕小等原因也限制富文本编辑器的众多强大功能&#xff0c;所以要找的编辑器功能必须是精简的。 找了好久&#xff0c;发现qedit…

Java IO 系统

Java IO系统 File类 用来处理文件目录&#xff0c;既可以代表一个特定文件的名称&#xff0c;也可以代表一组文件的名称&#xff0c;如果代表的是一个文件组&#xff0c;可以调用File.list()方法返回一个字符数组。 list()不传递任何参数时返回该目录下所有文件或文件名的字…

javascript/jquery高度宽度详情解说分析

为什么80%的码农都做不了架构师&#xff1f;>>> 一、window对象表示浏览器中打开的窗口 二、window对象可以省略 一、document对象是window对象的一部分 二、浏览器的HTML文档成为Document对象 window.location和document.location window对象的location属性引用的…

红黑树插入时的自平衡

红黑树插入时的自平衡 红黑树实质上是一棵自平衡的二叉查找树&#xff0c;引入带颜色的节点也是为了方便在进行插入或删除操作时&#xff0c;如果破坏了二叉查找树的平衡性能通过一系列变换保持平衡。 红黑树的性质 每个节点要么是红色&#xff0c;要么是黑色根节点必须是黑…

UWP学习记录

微软{X:Bind}、{Binding}资料网站 &#xff1a; https://msdn.microsoft.com/windows/uwp/xaml-platform/x-bind-markup-extension在View的ItemTemplate中绑定ViewModel的方法&#xff1a;1 <ItemsControl Name"XX" ItemsSource"{x:Bind VM.XXModels,ModeOne…

【Java】HashMap源码(1.7)

Life is not a ridiculous number of life, the meaning of life lies in life itself HashMap源码 散列集 数组和链表可以保持元素插入的顺序&#xff0c;对数组来说&#xff0c;他的优点是拥有连续的存储空间&#xff0c;因此可以使用元素下标快速访问&#xff0c;但缺点在…

画刷的使用

1.画刷的定义&#xff1a; HBRUSH hBrush; windows 自定义的画刷&#xff1a; WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH &#xff08;也叫HOLLOW_BRUSH&#xff09; 获取方法如下&#xff1a; hBrush (HBRUSH) GetStockObject (GRAY_BR…

runtime官方文档

OC是一种面向对象的动态语言&#xff0c;作为初学者可能大多数人对面向对象这个概念理解的比较深&#xff0c;而对OC是动态语言这一特性了解的比较少。那么什么是动态语言&#xff1f;动态语言就是在运行时来执行静态语言的编译链接的工作。这就要求除了编译器之外还要有一种运…

【Java】synchronized关键字笔记

Java Synchronized 关键字 壹. Java并发编程存在的问题 1. 可见性问题 可见性问题是指一个线程不能立刻拿到另外一个线程对共享变量的修改的结果。 如&#xff1a; package Note.concurrency;public class Demo07 {private static boolean s true;public static void mai…

SQL Server-数据类型(七)

前言 前面几篇文章我们讲解了索引有关知识&#xff0c;这一节我们再继续我们下面内容讲解&#xff0c;简短的内容&#xff0c;深入的理解&#xff0c;Always to review the basics。 数据类型 SQL Server支持两种字符数据类型&#xff0c;一种是常规&#xff0c;另外一种则是Un…

pb retrieve时停止工作_大佬们挂在嘴边的PE、PB是什么?

在紧锣密鼓地准备科创50ETF的发行工作间隙&#xff0c;今天小夏先带你读懂最简单的PE、PB估值指标这两大指标。01、什么是PE&#xff08;市盈率&#xff09;PE&#xff0c;也就是市价盈利比率&#xff0c;简称市盈率。市盈率是指股票价格与每股收益&#xff08;每股收益&#x…

【设计模式 01】简单工厂模式(Simple factory pattern)

简单工厂模式 可以根据参数的不同返回不同类的实例 参考&#xff1a; CSDN|简单工厂模式 简单工厂通过传给工厂类的参数的不同&#xff0c;返回不同的对象&#xff0c;包括三部分组成&#xff1a; 具体的”产品“工厂类&#xff08;实例化并返回”产品“&#xff09;客户端&am…

用Visual Studio 2019连接 WSL来编译调试C/C++项目

因为有作业要在Linux环境下写&#xff0c;用虚拟机直接卡成PPT&#xff0c;VS code又不会调试&#xff0c;就搞一下VS 2019吧。 环境 windows 10 WSL(Ubuntu 18.04.4) Visual Studio Community 2019 Linux 里要有C/C环境&#xff08;gcc等&#xff09;VS要有 适用于 Linux…

系统移植的四大步骤

最近在学习系统移植的相关知识&#xff0c;在学习和调试过程中&#xff0c;发现了很多问题&#xff0c;也解决了很多问题&#xff0c;但总是对于我们的开发结果有一种莫名其妙的感觉&#xff0c;纠其原因&#xff0c;主要对于我们的开发环境没有一个深刻的认识&#xff0c;有时…

display:flex

flex&#xff1a;弹性布局 常用属性介绍&#xff1a; flex-direction: column &#xff08;设置主容器主轴方向&#xff09; flex-flow: row wrap &#xff08;第一个参数为flex-direction&#xff0c;第二个为flex-wrap&#xff09; align-items: flex-start &#xff08;设…

Linux(Ubuntu 19.10)下 Qt5 连接 MySQL(QMYSQL driver not loaded)

Linux&#xff08;Ubuntu 19.10&#xff09;下 Qt5 连接 MySQL 安装好 MySQL 和 Qt Qt 连接 MySQL 的代码 QSqlDatabase dQSqlDatabase::addDatabase("QMYSQL");//加载mysql驱动&#xff0c;这个字符串是固定的 d.setHostName("127.0.0.1"); d.setDatabas…

Wpf 数据绑定简介、实例1

简介&#xff1a;1.WPF绑定使用的源属性必须是依赖项属性&#xff0c;这是因为依赖项属性具有内置的更改通知支持&#xff0c;元素绑定表达式使用了Xaml扩展标记&#xff0c; WPF绑定一个控件是使用Binding.ElementName, 绑定非控件对象时使用Source,RelativeSource,DataContex…

【设计模式 04】代理模式

代理模式 代理模式( Proxy)&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。 参考&#xff1a;refactoringguru | proxy 什么是代理模式 有时候如果想要访问某个对象&#xff0c;但又没办法直接访问或不方便直接访问&#xff0c;可以使用代理模式&#xff0c;代理…

css 大于号 标签_CSS设计基础选择器篇

点击上方 Java项目学习 &#xff0c;选择 星标 公众号重磅资讯、干货&#xff0c;第一时间送达前言&#xff1a;如果将CSS样式应用于特定的网页对象上&#xff0c;需要先找到目标元素。在CSS样式中执行这一任务的部分被称为选择器。1 标签选择器优点&#xff1a;为页面中同类型…