DES 加密算法以及实现
文章目录
- DES 加密算法以及实现
- 1. 中文版介绍
- 2. 英文版介绍
- 1.1 Subkeys generation
- 1.2 Data encryption
- 3. 算法实践
- 3.1 读取置换矩阵
- 3.2 置换
- 3.3 子密钥生成以及循环左移
- 3.4 加密和解密
- 4. 示例
- 5. 代码
- 推荐文章
1. 中文版介绍
2. 英文版介绍
自己根据英文材料简单概括的写了一下,若有出错的地方,还请各位斧正
DES, known as Data Encryption Standard , is designed to encipher and decipher the 64-bit blocks under the control of a 64-bit key. Generally, the encryption/decrytion process can be sparated into two chuncks. Subkeys generation and encipherment of data.
1.1 Subkeys generation
When the inputted key is firstly permuted (pc1), its length is shorted to 56 bits by removing the parity check bits. Then it is split into two parts C0 and D0 with equal length, where the left shift will be performed 16 times. Finally, a list of subkeys (K1 … Kn) can be obtainted.
1.2 Data encryption
Similarly, an initial permtation is performed on the 64-bit inputted data, which will be also separated into two part with equal length L0 nad R0. More importantly, the next part, basically a round function, is iterated 16 times, and within each iteration an XOR cumputation is performed. This XOR takes the parameters of the left part of last iteration and result of a funtion() . This particuar function can be illustrated as below:
At first, this function is permutated by E table, which can extend the inputted R(i-1) to 48 bits. Then another XOR computation is implemented with another parameter K(i). Finally, every block of 6 bits B(1-8) is taken by S(1-8) to get a result of 4 bits. The S operation is actually finding the value at a specified position on a given table with the inputted 6-bit data. The first and the last bits are concatenate to show a decimal number within 0 to 3, while the middle four bits can indicate a decimal number within 0 to 15. The former is the index of row and the latter is the index of column in the table. After 16 iterations, the final result is got by R16 at left side and L16 at right side concatenatd, and performed an inverse initial permutation.
3. 算法实践
代码整体结构(文章结尾会附上全部代码)
对于DES算法的实现,本次实现考虑了基本的面向对象思想,其中一个名为DES的类负责逻辑处理,而UI类负责处理UI组件并控制I/O。此外,DES类中的数据和键大多是一个整数列表,如[1,0,…1, 0]。这样易于控制和计算。而字符串和十六进制、十六进制和二进制之间的转换,以及随机密钥生成都在主UI类中处理。算法中需要用到多个置换矩阵,这些矩阵可以从一个permutation.txt中读取,它大概长这样
算法实现的基本思想为: 如果加密前和解密后两个比特序列相同,则整个加密和解密过程可以被证明是正确的
此外,如果输入的明文转成二进制不够64位的话需要填0补充到64位,如果超过64位则截成多个64位的段然后分别进行DES加密(main.py中实现)
3.1 读取置换矩阵
def __readTables(self):
# read contents
with open("permutation.txt", 'r') as f:
raw = f.read().split('--\n')
for form in raw:
# loop each permutation
results = []
lines = form.split("\n")
pattern_name = lines[0]
for content in lines[1:]:
# loop each line in each permuatation
lst = content.split(" ")
pure_lst = [int(x) for x in lst if x.strip() != '']
# turn into Integer
if len(pure_lst) != 0:
results.append(pure_lst)
self.patterns[pattern_name] = results
return None
将permutation.txt中的内容读取到一个字典中(self.patterns)
3.2 置换
```python
def __permutation(self, data, pattern):
# pattern is a matrix
new = []
for i in range(len(pattern)):
for j in range(len(pattern[i])):
position = pattern[i][j]
new.append(int(data[position - 1]))
return new
这是至关重要的一步,循环遍历pattern(矩阵),利用pattern提供的位置转换数据。一些操作如置换选择1和2、初始置换、e-bit selection等都应采用该方法来达到目的
3.3 子密钥生成以及循环左移
def __subKeyGeneration(self):
# generate subKeys
pc1 = self.patterns.get('pc1')
pc2 = self.patterns.get('pc2')
shift = self.patterns.get('shift')
self.currentKey = self.__permutation(self.currentKey, pc1)
self.subKeys = self.__leftShiftAndPermutation(self.currentKey, shift, pc2)
return None
def __leftShiftAndPermutation(self, data, pattern, pc2):
# pattern, pc2 is a matrix
new = []
length = int(len(data) / 2)
# split into two parts
c = data[0:length]
d = data[length:]
for i in pattern:
# transformation
c_front = c[0:i[1]]
c_back = c[i[1]:]
c = c_back + c_front
d_front = d[0:i[1]]
d_back = d[i[1]:]
d = d_back + d_front
# call permutation functions
key = self.__permutation(c + d, pc2)
new.append(key)
return new
此方法比较简单,只是取pc1,根据移位矩阵进行左移位,最后再进行pc2得到结果
3.4 加密和解密
def encrypt(self):
# encryption
if not self.__encrypted:
self.__subKeyGeneration()
# initial permutation (IP)
ip = self.patterns.get('ip')
new_txt = self.__permutation(self.currentTxt, ip)
# left and right
length = int(len(new_txt) / 2)
l = new_txt[0:length]
r = new_txt[length:]
# the very important step (16 rounds)
for key in self.subKeys:
tempr = r
r = self.__xorComputation(l, self.__funtionF(key, tempr))
l = tempr
result = r + l
# the last ip-1 permutation
result = self.__permutation(result, self.patterns.get('ip-1'))
return result
else:
return None
def decrypt(self):
# decryption
if self.__encrypted:
# very similar to encrypt
self.__subKeyGeneration()
ip = self.patterns.get('ip')
new_txt = self.__permutation(self.currentTxt, ip)
length = int(len(new_txt) / 2)
l = new_txt[0:length]
r = new_txt[length:]
# notice the loop order of subKeys should be reversed
for key in self.subKeys[::-1]:
tempr = r
r = self.__xorComputation(l, self.__funtionF(key, tempr))
l = tempr
result = r + l
result = self.__permutation(result, self.patterns.get('ip-1'))
return result
else:
return None
通过对上述几个函数的解释,可以清楚地了解加密步骤。它生成sukeys,进行初始置换,拆分为Left和Right,使用XOR和函数F迭代16轮,最终进行ip-1置换。加密和解密之间唯一的区别是子密钥的循环顺序是颠倒的(for key in subkeys / subkeys[::-1])。
4. 示例
- 首先点击
Generate Key
按钮来获取一个随机的Key。 - 在左侧输入框中输入任意长度的文本。
- 记录密文或直接将此密文放入右侧输入框
- 将解密后的文本与原始输入的文本进行比较
5. 代码
main.py
import random
from DES import DES
import tkinter as tk
from tkinter import RIDGE, DISABLED, FLAT, NORMAL
def encode(s):
# turn string into binary
return ''.join([bin(ord(i)).replace('0b', '') for i in s])
def randomKey():
# generate a random key
str_hex = ''
str_bin = ''
for i in range(16):
num = random.randint(0, 15)
str_hex += str(hex(num)).replace('0x', '')
binary = bin(num)
if (len(binary) - 2) == 4:
str_bin += str(binary).replace('0b', '')
else:
num = 6 - len(binary)
str_bin += str(binary).replace('0b', num * '0')
return [str_hex, str_bin]
class UI:
# the content of the text input
def __init__(self):
# window and instances
self.currentText = None
self.currentCipher = None
self.currentKey = None
self.textList = []
self.cipherList = []
self.results = {}
self.root = tk.Tk()
self.root.title('DES Demonstrator')
self.root.geometry("900x450+350+80")
# text and cipher
self.entry_txt_var = tk.StringVar()
self.entry_cipher_var = tk.StringVar()
self.entry_txt = tk.Entry(self.root, textvariable=self.entry_txt_var)
self.entry_cipher = tk.Entry(self.root, textvariable=self.entry_cipher_var)
self.text_txt = tk.Text(self.root, relief=FLAT)
self.text_cipher = tk.Text(self.root, relief=FLAT)
self.label_txt_input = tk.Label(self.root, text="Enter text: ")
self.label_cipher_input = tk.Label(self.root, text="Enter cipher: ")
self.label_txt_output = tk.Label(self.root, text="Get cipher: ")
self.label_cipher_output = tk.Label(self.root, text="Get text: ")
self.label_text_notification = tk.Label(self.root, text="Please input yor text")
self.label_cipher_notification = tk.Label(self.root, text="Please input yor cipher")
self.btn_txt = tk.Button(self.root, text="Encrypt", relief=RIDGE, command=self.txt_event)
self.btn_cipher = tk.Button(self.root, text="Decrypt", relief=RIDGE, command=self.cipher_event)
# key
self.label_key = tk.Label(self.root, text="Key: ")
self.label_hex = tk.Label(self.root, text="(Hexadecimal)")
self.label_bin = tk.Label(self.root, text="(Binary)")
self.text_key_1 = tk.Text(self.root, relief=FLAT)
self.text_key_2 = tk.Text(self.root, relief=FLAT)
self.btn_key = tk.Button(self.root, text="Generate Key", relief=RIDGE, command=self.key_event)
# layout
self.label_key.place(x=80, y=305)
self.btn_key.place(x=680, y=285, height=70, width=100)
self.text_key_1.place(x=140, y=280, height=30, width=400)
self.text_key_2.place(x=140, y=330, height=30, width=400)
self.label_text_notification.place(x=190, y=15)
self.label_cipher_notification.place(x=625, y=15)
self.label_hex.place(x=390, y=280, height=30, width=400)
self.label_bin.place(x=390, y=330, height=30, width=400)
self.label_txt_input.place(x=80, y=55)
self.label_cipher_input.place(x=515, y=55)
self.label_txt_output.place(x=25, y=185)
self.label_cipher_output.place(x=490, y=185)
self.btn_txt.place(x=200, y=100, height=50, width=100)
self.btn_cipher.place(x=650, y=100, height=50, width=100)
self.entry_txt.place(x=150, y=50, height=30, width=200)
self.entry_cipher.place(x=600, y=50, height=30, width=200)
self.text_txt.place(x=100, y=180, height=30, width=300)
self.text_cipher.place(x=550, y=180, height=30, width=300)
self.root.resizable(False, False)
self.text_txt.config(state=DISABLED)
self.text_cipher.config(state=DISABLED)
self.text_key_1.config(state=DISABLED)
self.text_key_2.config(state=DISABLED)
def txt_event(self):
tempText = self.entry_txt_var.get()
if len(tempText) == 0:
# not inputted text
self.label_text_notification.config(text="Absent content", fg="red")
else:
if self.currentKey != None:
# when the encryption can be run successfully
self.label_text_notification.config(text="Please input yor text", fg="black")
self.currentText = tempText
# every time the buttion is clicked, an encryption will be performed
self.encryption()
else:
# no key
self.label_text_notification.config(text="Generate key first", fg="red")
def cipher_event(self):
tempCipher = self.entry_cipher_var.get()
if len(tempCipher) == 0:
# not inputted text
self.label_cipher_notification.config(text="Absent content", fg="red")
else:
if self.currentKey != None:
# when the decryption can be run successfully
self.label_cipher_notification.config(text="Please input yor cipher", fg="black")
self.currentCipher = tempCipher
# every time the buttion is clicked, an decryption will be performed
self.decryption()
else:
# no key
self.label_cipher_notification.config(text="Generate key first", fg="red")
def key_event(self):
# get a random key
key_random = randomKey()
self.currentKey = key_random[1]
self.changeContent(self.text_key_1, key_random[0])
self.changeContent(self.text_key_2, key_random[1])
def encryption(self):
# turn into binary
binary = encode(self.currentText)
# call dataPreparation
self.textList.clear()
self.dataPreparation(binary)
# results handling
index_binary = ''
entire_cipher = []
for i in self.textList:
# create index
index_binary += i
tempDes = DES(i, self.currentKey, False)
cipher = tempDes.encrypt()
entire_cipher += cipher
# string result in binary
result = ''.join([str(x) for x in entire_cipher])
# string result in hexadecimal
hex_result = ''
for i in range(len(entire_cipher)):
if i % 4 == 0:
str_bin = ''.join([str(j) for j in entire_cipher[i : i+4]])
hex_result += hex(int(str_bin, 2)).replace('0x', '')
# display it on UI
self.changeContent(self.text_txt, hex_result)
# add to the results
self.results[index_binary] =self.currentText
def decryption(self):
# turn hexadecimal into binary
binary = ''
for i in self.currentCipher:
tempBin = bin(int(i, 16))
if (len(tempBin) - 2) == 4:
binary += str(tempBin).replace('0b', '')
else:
num = 6 - len(tempBin)
binary += str(tempBin).replace('0b', num * '0')
# trun into groups
self.cipherList.clear()
self.cipherPreparation(binary)
# results handling
entire_text = []
for i in self.cipherList:
tempDes = DES(i, self.currentKey, True)
text = tempDes.decrypt()
entire_text += text
# string result in binary
result = ''.join([str(x) for x in entire_text])
# get the original text
original_text = self.results.get(result)
# error handling
if original_text == None:
self.label_cipher_notification.config(text="Key has been changed", fg="red")
original_text = ''
# display it on UI
self.changeContent(self.text_cipher, original_text)
def changeContent(self, widget, content):
# change content of a specified text field
widget.config(state=NORMAL)
widget.delete('0.0', 'end')
widget.insert('0.0', content)
widget.config(state=DISABLED)
def dataPreparation(self, data):
# split into a list of 64 bits when longer than 64 bits,
# or add up to 64 bits with '0' when shorter than 64 bits
if len(data) <= 64:
# end this recursion
remain = 64 - len(data)
data += remain * '0'
self.textList.append(data)
return self.textList
else:
present = data[0:64]
forward = data[64:]
self.textList.append(present)
return self.dataPreparation(forward)
def cipherPreparation(self, data):
# same as above function
if len(data) <= 64:
# end this recursion
remain = 64 - len(data)
data += remain * '0'
self.cipherList.append(data)
return self.cipherList
else:
present = data[0:64]
forward = data[64:]
self.cipherList.append(present)
return self.cipherPreparation(forward)
if __name__ == '__main__':
GUI = UI()
GUI.root.mainloop()
DES.py
class DES:
# txt, key are string , encrypted are boolean
# utility matrices are put into patterns
patterns = {}
currentKey = []
currentTxt = []
subKeys = []
def __init__(self, txt, key, encrypted):
self.__txt = txt
self.__key = key
self.__encrypted = encrypted
self.__readTables()
self.__readIntoList()
def getTxt(self):
return self.__txt
def getKey(self):
return self.__key
def getStatus(self):
return self.__encrypted
def __readTables(self):
# read contents
with open("permutation.txt", 'r') as f:
raw = f.read().split('--\n')
for form in raw:
# loop each permutation
results = []
lines = form.split("\n")
pattern_name = lines[0]
for content in lines[1:]:
# loop each line in each permuatation
lst = content.split(" ")
pure_lst = [int(x) for x in lst if x.strip() != '']
# turn into Integer
if len(pure_lst) != 0:
results.append(pure_lst)
self.patterns[pattern_name] = results
return None
def __readIntoList(self):
# turn str into list [1, 0, 0 1 ......]
self.currentKey.clear()
self.currentTxt.clear()
for i in self.__key:
self.currentKey.append(int(i))
for j in self.__txt:
self.currentTxt.append(int(j))
return None
def encrypt(self):
# encryption
if not self.__encrypted:
self.__subKeyGeneration()
# initial permutation (IP)
ip = self.patterns.get('ip')
new_txt = self.__permutation(self.currentTxt, ip)
# left and right
length = int(len(new_txt) / 2)
l = new_txt[0:length]
r = new_txt[length:]
# the very important step (16 rounds)
for key in self.subKeys:
tempr = r
r = self.__xorComputation(l, self.__funtionF(key, tempr))
l = tempr
result = r + l
# the last ip-1 permutation
result = self.__permutation(result, self.patterns.get('ip-1'))
return result
else:
return None
def decrypt(self):
# decryption
if self.__encrypted:
# very similar to encrypt
self.__subKeyGeneration()
ip = self.patterns.get('ip')
new_txt = self.__permutation(self.currentTxt, ip)
length = int(len(new_txt) / 2)
l = new_txt[0:length]
r = new_txt[length:]
# notice the loop order of subKeys should be reversed
for key in self.subKeys[::-1]:
tempr = r
r = self.__xorComputation(l, self.__funtionF(key, tempr))
l = tempr
result = r + l
result = self.__permutation(result, self.patterns.get('ip-1'))
return result
else:
return None
def __funtionF(self, key, data):
# F function
eTable = self.patterns.get('etable')
# E bit-selection
extension = self.__permutation(data, eTable)
# XOR
xor = self.__xorComputation(key, extension)
# S boxes (S1 S2 ... S8)
num = 1
value = [] # 32 bits
for i in range(len(xor)):
if i % 6 == 0:
pos = xor[i: i + 6]
# different S box
s = self.patterns.get('s' + str(num))
value += self.__extractFromSbox(pos, s)
num += 1
# the last p permuatation
value = self.__permutation(value, self.patterns.get('p'))
return value
def __extractFromSbox(self, pos, s):
row = str(pos[0]) + str(pos[5])
column = str(pos[1]) + str(pos[2]) + str(pos[3]) + str(pos[4])
# from binary into decimal
row_dec = int(row, 2)
col_dec = int(column, 2)
# get the value in the table
value = bin(s[row_dec][col_dec])
if (len(value) - 2) == 4:
value = value.replace('0b', '')
else:
num = 6 - len(value)
value= value.replace('0b', num * '0')
return [int(x) for x in value]
def __xorComputation(self, key, data):
# XOR
xor = []
for i in range(len(data)):
if key[i] != data[i]:
xor.append(1)
else:
xor.append(0)
return xor
def __subKeyGeneration(self):
# generate subKeys
pc1 = self.patterns.get('pc1')
pc2 = self.patterns.get('pc2')
shift = self.patterns.get('shift')
self.currentKey = self.__permutation(self.currentKey, pc1)
self.subKeys = self.__leftShiftAndPermutation(self.currentKey, shift, pc2)
return None
def __permutation(self, data, pattern):
# pattern is a matrix
new = []
for i in range(len(pattern)):
for j in range(len(pattern[i])):
position = pattern[i][j]
new.append(int(data[position - 1]))
return new
def __leftShiftAndPermutation(self, data, pattern, pc2):
# pattern, pc2 is a matrix
new = []
length = int(len(data) / 2)
# split into two parts
c = data[0:length]
d = data[length:]
for i in pattern:
# transformation
c_front = c[0:i[1]]
c_back = c[i[1]:]
c = c_back + c_front
d_front = d[0:i[1]]
d_back = d[i[1]:]
d = d_back + d_front
# call permutation functions
key = self.__permutation(c + d, pc2)
new.append(key)
return new
pc1
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
--
shift
1 1
2 1
3 2
4 2
5 2
6 2
7 2
8 2
9 1
10 2
11 2
12 2
13 2
14 2
15 2
16 1
--
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
--
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
--
etable
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
--
s1
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
--
s2
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
--
s3
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
--
s4
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
--
s5
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
--
s6
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
--
s7
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
--
s8
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
--
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 2
--
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