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
每一輪循環加密的過程為:
- 將初態置換后或上一次循環后得到的64位塊分成左右各32位的子塊Left盒Right
- Right經過f函數轉換后得到一個32位的串,這個串與Left做異或后得到下一輪循環的Right
- 將這一輪原視的Right作為下一輪的Left
- 拼接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函數有三部分組成:
- 拓展置換
- S盒置換
- 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盒置換有兩步:
- 與密鑰keyi{key}_ikeyi?做異或
- 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表
Round | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Rotations | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 1 |
- 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(分組鏈接模式),還有CFB,OFB等
- 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