一、比特序列(0和1排列而成)运算
1.编码(≠加密)过程
明文字母→ASCII(十进制)→二进制
二、XOR运算(异或)
1.一个比特的异或
0 XOR 0 = 0
1 XOR 0 = 1
0 XOR 1 = 1
1 XOR 1 = 0
2.多个比特的异或(一一对应)
0 1 0 1 0 1 0 1 0 1
XOR
0 1 0 1 0 1 1 0 1 0
=
0 0 0 0 0 0 1 1 1 1
3.不可推测的蒙版中的比特序列是随机数
三、DES
1.加密和解密
将64比特的明文加密成64比特的密文,规格上来所密钥长度是64比特,但是每隔7个比特会有一个比特用来校验,校验位为8,16,24,32,40,48,56,64,因此实际上密钥只有56比特。
64比特的单位称为分组。
迭代加密称为模式。
2.结构(Feistel网络)
3.具体算法http://【金山文档 | WPS云文档】 DES加密算法 https://kdocs.cn/l/ci9U9bGrd8FX
DES
基本原理
-
DES算法是一个分组加密的对称密码算法。
-
密钥长64位,56位参与运算,其余8位为校验位。(8,16,24,32,40,48,56,64)
-
当n个64位明文数据块都经过DES加密处理后,所得到的n个64位密文数据块串在一起就是密文。
明文IP置换
先将明文换成二进制
#转化为二进制数的代码解释 plain = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in plain]) ''' for i in plain: 对于 plain 列表中的每个元素 i 进行迭代。 bin(i): 将整数 i 转换为二进制字符串。 [2:]: 去除二进制字符串中的前缀 "0b"。 zfill(8): 在二进制字符串的左侧填充零,使其总长度为 8。 list(map(int, ...)): 将二进制字符串中的每个字符转换为整数,并将结果放入一个列表。 [...] for i in plain: 将步骤 5 中的结果放入一个列表,形成一个二维列表。 reduce(add, ...): 使用 reduce 函数和 add 函数对步骤 6 中的二维列表进行累加,将所有的二进制位连接成一个大的二进制列表。 '''
按照⼀定的规则,将原来的64位⼆进制明文重新排序。
_IP = [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,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
def IP(plain: List[int]):
return list(map(lambda x: plain[x], _IP))
'''
lambda x: plain[x]是一个匿名函数,它接受一个参数x,并返回plain[x]的值。在这里,x表示_IP列表中的索引值,plain[x]表示根据索引值从plain列表中获取对应的元素。
map(lambda x: plain[x], _IP)的作用是将_IP列表中的每个元素作为索引值,通过匿名函数映射到plain列表中的对应元素,最终返回一个映射后的结果列表。
plain = b'12345678'
plain = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in plain])
'''
64位均分为两组,一组32位,即为L。和R。。
block = IP(plain)
L, R = block[:32], block[32:] #切片操作
'''
调用IP函数,将置换后的明文plain赋值给block
再将block分为L R 两部分
[:32]可以返回该列表从开头到第 31 个元素(索引为 0 开始计数,所以是到索引为 31 的元素)组成的新列表
'''
轮函数——E扩展置换
将R的32位输⼊扩展为48位输出。
#E扩展表
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
]
def EP(data: List[int]): # 扩展置换
return list(map(lambda x: data[x], __expansion_table)) #x表示_EP列表中的索引值
轮函数——S盒压缩处理
经过扩展的48位明⽂和48位密钥进⾏异或运算。
R = EP(R) # 扩展置换
R = list(map(lambda x, y: x ^ y, R, skeys[index]))
# 异或
'''
map(lambda x, y: x ^ y, R, skeys[index])
首先,map() 函数在这里被用来对两个序列中的对应元素应用一个匿名函数 lambda x, y: x ^ y。
这里的 x 来自于经过扩展置换后的 R,
y 来自于 skeys[index] (假设 skeys 是一个列表 [10, 20, 30, 40, 50],如果 index 的值为 2,那么 skeys[index] 就会返回 30)。
匿名函数 lambda x, y: x ^ y 的作用是对传入的两个参数 x 和 y 进行按位异或操作。所以这行代码整体上就是对 R 和 skeys[index] 这两个序列的对应元素逐一进行按位异或操作,然后将得到的结果组成一个新的序列(通过 list() 函数将 map() 函数返回的可迭代对象转换为列表),并重新赋值给 R。
'''
'''
相关密钥生成函数
def get_sub_key(key: List[int]):
key = PC_1(key) # PC-1置换
L, R = key[:28], key[28:] # 分成两半
skeys = []
for i in range(16):
for j in range(ROTATIONS[i]): # 根据轮次左移
L = L[1:] + L[:1] #将 L 原来的第一个元素移到了序列的末尾。
R = R[1:] + R[:1]
skeys.append(PC_2(L+R)) # PC-2置换
return skeys
'''
将48位输⼊等分为8块,每块6位输⼊压缩为4位输出,得到32 位数据。
#将48位输入等分为8块,每块6位输入压缩为4位输出
def F(index: int, R: List[int], skeys: List[List[int]]):
"""
index: 代表这是第几轮
R: 输入数据
skeys: 子密钥数组
"""
R = EP(R) # 扩展置换
R = list(map(lambda x, y: x ^ y, R, skeys[index])) # 异或
B = [R[:6], R[6:12], R[12:18], R[18:24], R[24:30], R[30:36], R[36:42], R[42:]] # 分成八份
Bn = [0] * 32 #创建一个新的列表 Bn,并将其初始化为包含 32 个元素,且每个元素的值都为 0。这个列表将在后续基于 S 盒的处理中用于存储中间结果。
pos = 0 #定义一个变量 pos(通常可以理解为 “位置”“指针” 等含义记录位置),并将其初始值赋为 0。
for i in range(8): #8次s盒循环
# 计算该使用S盒的行坐标和列坐标
row = (B[i][0] << 1) + B[i][5] #组合六位的首末位
#首先根据 B 列表中第 i 个子部分(即 B[i])的第一个元素 B[i][0] 和第六个元素 B[i][5]来计算用于 S 盒操作的行坐标。(计算格式:假如假设 B[i][0] 的值为整数 1,1 的二进制表示为 1。当进行 B[i][0] << 1 操作时,将其二进制位向左移动 1 位,得到 10,也就是十进制的 2,再加上B[i][5]=1,十进制依然是1,则row=3)
'左移操作原理'
'当对一个整数进行左移操作时,它在二进制表示下,所有的位都向左移动指定的位数(这里是 1 位),右边空出的位用 0 填充。
col = (B[i][1] << 3) + (B[i][2] << 2) + (B[i][3] << 1) + B[i][4] #组合中间四位形成新的二进制
sb = __sbox[i][(row << 4) + col] #sb是s盒中的十进制数字
Bn[pos + 0] = (sb & 8) >> 3 # 四位输出,首先 sb & 8 是进行按位与操作,它会将 sb 中的每一位与二进制数 1000(十进制的 8)进行按位与运算,这样做的结果是只会保留 sb 中对应二进制位为 1 的那些位
Bn[pos + 1] = (sb & 4) >> 2
Bn[pos + 2] = (sb & 2) >> 1
Bn[pos + 3] = (sb & 1)
'与:是一种二进制位运算
pos += 4
R = P(Bn)
return R
第3行第15列是13,转化为二进制就是1101,这样进行8次,得到32位密文。
P盒置换
S盒位再经过P盒置换。⾄此,⼀次轮函数操作完毕。
__p = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23,13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]#P置换表
def P(data: List[int]): # P置换
return list(map(lambda x: data[x], __p))
与L0进行异或,并将值赋给R1
for i in range(16):
tpR = R[:]#将R的值存给tpR
R = F(i, R, skeys)
R = list(map(lambda x, y: x ^ y, R, L))#将加密后的数与L进行异或并存给R
L = tpR#将tcR的值赋给L,即交换R和L
IP逆置换
#逆置换表
_FP = [
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,
32, 0, 40, 8, 48, 16, 56, 24
]
def FP(plain: List[int]):
return list(map(lambda x: plain[x], _FP))
密钥生成
#其他代码中的
//轮密钥产生
void produceKey(char key[8],char keyL[16][48])
{
char bit[64],keyout[56],C[28],D[28],temp[64];
char8Tobit64(key,bit);
//补8位奇偶校验位
for(uchar j=0;j<8;j++)
{
memcpy(temp+j*8,bit+j*7,7);
temp[j*8+7]=1;
}
memcpy(bit,temp,64);
//64--->56
reversePC_1(bit,keyout);
memcpy(C,keyout,28);
memcpy(D,keyout+28,28);
for(uchar i=0;i<16;i++)
{
leftMove(C,D,Left_Move[i]);
reversePC_2(C,D,keyL[i]);
}
}
密钥为64位,进行PC-1置换除去8位校验位,剩余56位参与运算。
#首先据表PC-1进行置换
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]#密钥置换表1
def get_sub_key(key: List[int]):
key = PC_1(key) # PC-1置换
56位的密钥分成两组分别为28位,C。=1111000 0110011 0010101 0101111
和D。=0101010 1011001 1001111 0001111。
#分为c1,c2
L, R = key[:28], key[28:] # 分成两半
根据移位次数表进行循环左移得到C1和D1~C16和D16。
C1=1110000 1100110 0101010 1011111
D1=1010101 0110011 0011110 0011110
#循环左移
ROTATIONS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]#左移表
for i in range(16):
for j in range(ROTATIONS[i]): # 根据轮次左移
L = L[1:] + L[:1]
R = R[1:] + R[:1]
#L[1:]:这会获取列表 L 中除了第一个元素之外的所有元素,组成一个新的列表。例如,如果 L = [1, 2, 3, 4],那么 L[1:] = [2, 3, 4]。 L[:1]:这会获取列表 L 中仅第一个元素,组成一个新的列表。例如,对于 L = [1, 2, 3, 4],L[:1] = [1]。 L[1:] + L[:1]:将前面得到的两个新列表进行拼接,得到的结果就是将原列表 L 的第一个元素移到了末尾,实现了循环左移一位的效果。例如,对于 L = [1, 2, 3, 4],经过 L[1:] + L[:1] 操作后,新的 L 变为 [2, 3, 4, 1]。
'''
代码包含两个嵌套的循环,外部循环迭代 16 次,内部循环迭代的次数由 ROTATIONS[i] 指定。
在每次内部循环中,L(左半部分)和 R(右半部分)分别执行了循环左移操作。
循环左移是指将二进制序列的位按照规定的位数向左移动,并将被移出的位重新放入序列的末尾。
'''
拼接C1和D1。再进行PC-2,48位参与PC-2运算,得到K1。
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
def PC_2(key: List[int]):
return list(map(lambda x: key[x], __pc2))
按照交换规则,⽣成16个48位的轮密钥:K1~K16。
结束。
from operator import add
from typing import List
from functools import reduce
_IP = [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,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
def IP(plain: List[int]): #根据_IP置换表对输入的明文列表进行初始置换操作,返回置换后的列表。
return list(map(lambda x: plain[x], _IP)) #通过map函数和 lambda 表达式,按照_IP表中的索引从输入的明文中取出对应元素,组成新的列表。
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
ROTATIONS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] #一个列表,指定了在生成子密钥过程中每一轮的左移位数。
def PC_1(key: List[int]):
return list(map(lambda x: key[x], __pc1))
def PC_2(key: List[int]):
return list(map(lambda x: key[x], __pc2))
def get_sub_key(key: List[int]): #生成用于加密过程的子密钥列表。首先对密钥进行 PC-1 置换,然后将其分成左右两部分,根据ROTATIONS列表指定的轮次进行左移操作,最后通过 PC-2 置换得到每一轮的子密钥并添加到列表中返回。
key = PC_1(key) # PC-1置换
L, R = key[:28], key[28:] # 分成两半
skeys = []
for i in range(16):
for j in range(ROTATIONS[i]): # 根据轮次左移
L = L[1:] + L[:1]
R = R[1:] + R[:1]
skeys.append(PC_2(L + R)) # PC-2置换
return skeys
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
] #扩展置换表,用于扩展数据以便后续与子密钥进行异或操作。
__sbox = [
# 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 = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23, 13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]
def EP(data: List[int]): # 扩展置换
return list(map(lambda x: data[x], __expansion_table))
def P(data: List[int]): # P置换
return list(map(lambda x: data[x], __p))
def F(index: int, R: List[int], skeys: List[List[int]]):
"""
index: 代表这是第几轮
R: 输入数据
skeys: 子密钥数组
"""
R = EP(R) # 扩展置换
R = list(map(lambda x, y: x ^ y, R, skeys[index])) # 异或
B = [R[:6], R[6:12], R[12:18], R[18:24], R[24:30], R[30:36], R[36:42], R[42:]] # 分成八份
Bn = [0] * 32
pos = 0
for i in range(8):
# 计算该使用S盒的行坐标和列坐标
row = (B[i][0] << 1) + B[i][5]
col = (B[i][1] << 3) + (B[i][2] << 2) + (B[i][3] << 1) + B[i][4]
sb = __sbox[i][(row << 4) + col]
Bn[pos + 0] = (sb & 8) >> 3 # 四位输出
Bn[pos + 1] = (sb & 4) >> 2
Bn[pos + 2] = (sb & 2) >> 1
Bn[pos + 3] = (sb & 1)
pos += 4
R = P(Bn)
return R
_FP = [
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,
32, 0, 40, 8, 48, 16, 56, 24
]
def FP(plain: List[int]):
return list(map(lambda x: plain[x], _FP))
key = b'12345678'
plain = b'12345678'
# 转为二进制数组
key = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in key])
plain = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in plain])
skeys = get_sub_key(key)
block = IP(plain)
L, R = block[:32], block[32:]
for i in range(16):
tpR = R[:]
R = F(i, R, skeys)
R = list(map(lambda x, y: x ^ y, R, L))
L = tpR
block = R + L
block = FP(block)
enc = bytes([int(''.join(map(str, block[i * 8:(i + 1) * 8])), 2) for i in range(8)])
print(enc)
四、三重DES(3DES)
1.加密流程
加密→解密→加密
2.解密流程
解密→加密→解密