多表代换密码原理 + python代码
文章目录
多表代换密码原理
多表代换密码首先将明文M分成由n个字母构成的分组 M 1 , M 2 , ⋯ , M j M_1,M_2,\cdots ,M_j M1,M2,⋯,Mj,
对每个分组的
M
i
M_i
Mi的加密为:
C
i
≡
A
M
i
+
B
(
m
o
d
N
)
,
i
=
1
,
2
,
⋯
,
j
C_i \equiv \mathbf{A}\,\mathbf{M}_i\; +\; \mathbf{B}\, (mod\;N)\;,\ i=1,2,\cdots,j
Ci≡AMi+B(modN), i=1,2,⋯,j
其中,(A,B)是密钥,A是n X n 的可逆矩阵,满足gcd(|A|,N)= 1,
B = ( B 1 , B 2 , ⋯ , B n ) T (B_1,B_2,\cdots,B_n)^T (B1,B2,⋯,Bn)T, C = ( C 1 , C 2 , ⋯ , C n ) T (C_1,C_2,\cdots,C_n)^T (C1,C2,⋯,Cn)T, M i \mathbf{M}_i Mi = ( m 1 , m 2 , ⋯ , m n ) T (m_1,m_2,\cdots,m_n)^T (m1,m2,⋯,mn)T.
对每个分组的
C
i
C_i
Ci的解密为:
M
i
≡
A
−
1
(
C
i
−
B
)
(
m
o
d
N
)
,
i
=
1
,
2
,
⋯
,
j
\mathbf{M}_i \equiv \mathbf{A}^{-1}\,(C_i-\mathbf{B})\, (mod\;N)\;,\ i=1,2,\cdots,j
Mi≡A−1(Ci−B)(modN), i=1,2,⋯,j
例如:
加密过程:
n = 3, N = 26
A
=
(
11
2
19
5
23
25
20
7
17
)
,
B
=
(
0
0
0
)
\mathbf{A} = \begin{pmatrix} 11 & 2 & 19 \\ 5 & 23 & 25 \\ 20 & 7 & 17 \end{pmatrix}, \mathbf{B} = \begin{pmatrix} 0 \\ 0 \\ 0 \end{pmatrix}
A=
115202237192517
,B=
000
message: you ……(此处就只举例第一个分组了)
you —> M 1 \mathbf{M}_1 M1 = ( 24 14 20 ) \begin{pmatrix} 24 \\ 14 \\ 20 \end{pmatrix} 241420 , C 1 \mathbf{C}_1 C1 = A \mathbf{A} A ( 24 14 20 ) \begin{pmatrix} 24 \\ 14 \\ 20 \end{pmatrix} 241420 = ( 22 6 8 ) \begin{pmatrix} 22 \\ 6 \\ 8 \end{pmatrix} 2268 . 我们可以知道22,6,8对应的是W,G,I. 加密就此完成。
解密过程:
解密时,先求出
A
−
1
=
(
11
2
19
5
23
25
20
7
17
)
−
1
=
(
10
23
7
15
9
22
5
9
17
)
\mathbf{A}^{-1} = \begin{pmatrix} 11 & 2 & 19 \\ 5 & 23 & 25 \\ 20 & 7 & 17 \end{pmatrix}^{-1} = \begin{pmatrix} 10 & 23 & 7 \\ 15 & 9 & 22 \\ 5 & 9 & 17 \end{pmatrix}
A−1=
115202237192517
−1=
10155239972217
再求:
M
1
\mathbf{M}_1
M1 =
A
−
1
\mathbf{A}^{-1}
A−1
(
22
6
8
)
\begin{pmatrix} 22 \\ 6 \\ 8 \end{pmatrix}
2268
=
(
24
14
20
)
\begin{pmatrix} 24 \\ 14 \\ 20 \end{pmatrix}
241420
.
(
24
14
20
)
\begin{pmatrix} 24 \\ 14 \\ 20 \end{pmatrix}
241420
—>you. 至此,解密完成。
代码部分:
引入numpy库
import numpy as np
将明文进行分组处理
def process_text(init_message, group_len):
""" 将文本进行分组处理 """
# 去除文本中的空格
init_message = init_message.replace(" ", "")
# 计算需要补充的字符数(当明文长度不能被分组长度整除时)
blank = len(init_message) % group_len
# 如果有分组长度不足,则补充字符 'z' 直到长度能够整除 group_len
if blank != 0:
init_message = init_message + (group_len - blank) * "z"
# 将字符串转换为小写,并将每个字符转换为对应的 ASCII 码减去 97
init_message_list = list(init_message.lower())
code_list = [ord(i) - 97 for i in init_message_list]
# 将 code_list 按照 group_len 进行分组
code_group = []
for i in range(0, len(code_list), group_len):
code_group.append(code_list[i:i + group_len])
return code_group
拓展欧几里得算法
def extended_gcd(a, b):
"""扩展欧几里德算法
接受两个整数 a 和 b 作为输入,并返回它们的最大公约数(GCD),以及满足 ax + by = gcd(a, b) 的系数 x 和 y。
"""
if b == 0:
return 1, 0
else:
x, y = extended_gcd(b, a % b)
x, y = y, x - (a // b) * y
return x, y
# 该函数也用于在 modular_inverse_element 函数中找到模反元素。
求乘法逆元
def modular_inverse_element(a: int, m=26):
"""求整数a关于1模m的乘法逆元"""
# 首先检查 a 和 m 是否互质 若不互质则返回-1
if (np.gcd(a, m) != 1): return -1
# 使用 extended_gcd 函数找到满足 a * inv_a + m * _ = 1 的系数 inv_a 和 _
inv_a, _ = extended_gcd(a, m)
inv_a %= m
return inv_a
加密计算
def polyalphabetic_substitution(code: list, A, B, m=26) -> list:
"""加密计算函数"""
group_len = len(B) # 获取密钥B的长度作为分组长度
code = np.mat(code).reshape(group_len, 1) # 将code转换为矩阵,并调整形状为(group_len, 1)
C = ((np.dot(A, code)) % m).reshape(-1) # 使用矩阵乘法和取模运算,计算加密后的结果C
# C = ((np.dot(A, code) + B) % m).reshape(-1) # 如果需要加上密钥B,则取消注释此行(但好像有点问题,暂时还没进行修改)
C = C.tolist()[0] # 将结果C转换为列表形式
return C
矩阵的乘法逆元
def modular_inverse_matrix(A, m=26):
"""求矩阵A关于1模m的乘法逆元"""
detA = np.linalg.det(A) # 计算矩阵A的行列式
inv_a = np.linalg.inv(A) # 计算矩阵A的逆矩阵
Adjoint = detA * inv_a # 计算矩阵A的伴随矩阵
inv_detA = modular_inverse_element(round(detA), m) # 计算行列式detA关于1模m的乘法逆元
cipher_inv_a = ((inv_detA % m) * Adjoint) % m # 计算矩阵A关于1模m的乘法逆元
cipher_inv_a = np.round(cipher_inv_a).astype(int) # 将结果四舍五入并转换为整数类型
return cipher_inv_a # 返回矩阵A关于1模m的乘法逆元
解密计算
def inverse_polyalphabetic_substitution(code: list, inv_a, B, m=26) -> list:
"""解密计算函数"""
group_len = len(B) # 获取密钥B的长度
code = np.mat(code).reshape(group_len, 1) # 将code转换为矩阵,并调整形状为(group_len, 1)
M = (np.dot(inv_a, (code)) % m).reshape(-1) # 解密计算,使用矩阵乘法和取模运算
# M = (np.dot(inv_a, (code - B)) % m).reshape(-1) # 如果需要减去密钥B,则取消注释此行(但好像有点问题,暂时还没进行修改)
M = M.tolist()[0] # 将结果M转换为列表形式
return M
加密函数
def encrypt_message(key_a, key_b, plaintext):
""" 加密函数 """
ciphertext = ""
code_group = process_text(plaintext, len(key_b))
for group in code_group:
group = polyalphabetic_substitution(group, key_a, key_b)
group = [chr(i + 97) for i in group] # 将数字转换为对应的字母
group = ''.join(group) # 将列表中的字母连接成字符串
ciphertext = ciphertext + group + " "
return ciphertext
解密函数
def decrypt_message(key_a, key_b, ciphertext):
""" 解密函数 """
plaintext = ""
code_group = process_text(ciphertext, len(key_b))
key_a_inv = modular_inverse_matrix(key_a, m=26)
for group in code_group:
group = inverse_polyalphabetic_substitution(group, key_a_inv, key_b)
group = [chr(i + 97) for i in group] # 将数字转换为对应的字母
group = ''.join(group) # 将列表中的字母连接成字符串
plaintext = plaintext + group + " "
return plaintext
实验测试
if __name__ == '__main__':
keyA = [[11, 2, 19],
[5, 23, 25],
[20, 7, 17]]
keyB = [0, 0, 0] # 这里B不为0时可能结果有错,暂时还未更改。
plaintext = "your pin no is four one two six"
ciphertext = "wgi fgj tmr lhh xth wbx zps brb"
print("密钥为:", "key_a:", keyA, "\n\t ", "key_b:", keyB)
print("加密为:", encrypt_message(keyA, keyB, plaintext))
print("解密为:", decrypt_message(keyA, keyB, ciphertext))
相关numpy库中函数的用法补充
numpy.mat( )
numpy.mat() 的主要作用是将输入解释为矩阵。与 numpy.array() 不同,numpy.mat() 会返回一个二维矩阵,而不是一个 n 维数组。
例如,如果你有一个列表 [1, 2, 3],你可以使用 numpy.mat() 将其转换为一个 1x3 的矩阵:
import numpy as np
list1 = [1, 2, 3]
matrix1 = np.mat(list1)
print(matrix1)
运行上述代码,你会得到以下输出:
[[1 2 3]]
numop.dot( )
numpy.dot函数用于计算两个数组的点积(内积)。
点积是指两个数组的对应元素相乘后再求和的结果。它可以用于计算向量的长度、计算矩阵的乘法等。
numpy.dot函数的语法如下:
numpy.dot(a, b, *out*=None)
其中,a和b是要计算点积的两个数组,可以是一维数组、二维数组或多维数组。out参数是可选的,用于指定计算结果的输出数组。
以下是一些示例:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.dot(a, b)
print(result) # 输出:32
c = np.array([[1, 2], [3, 4]])
d = np.array([[5, 6], [7, 8]])
result = np.dot(c, d)
print(result) # 输出:[[19 22]
# [43 50]]
在这些示例中,np.dot(a, b)计算了一维数组a和b的点积,结果为32。np.dot(c, d)计算了二维数组c和d的点积,结果为一个二维数组.
numpy.reshape( )
numpy.reshape()函数用于改变数组的形状,即重新排列数组的维度。它接受一个数组作为输入,并返回一个具有新形状的数组,而不改变原始数组的数据。
numpy.reshape()函数的语法如下:
numpy.reshape(array, newshape, order='C')
参数说明:
- array:要改变形状的数组。
- newshape:新的形状,可以是一个整数或一个整数元组。
- order:可选参数,指定数组在内存中的存储顺序。默认为’C’,表示按行存储。
下面展示一些示例:
import numpy as np
# 创建一个一维数组
arr = np.array([1, 2, 3, 4, 5, 6])
# 将一维数组转换为二维数组
new_arr = np.reshape(arr, (2, 3))
print(new_arr)
# 输出:
# [[1 2 3]
# [4 5 6]]
# 创建一个二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
# 将二维数组转换为三维数组
new_arr2 = np.reshape(arr2, (2, 3, 1))
print(new_arr2)
# 输出:
# [[[1]
# [2]
# [3]]
#
# [[4]
# [5]
# [6]]]
全部代码
import numpy as np
def process_text(init_message, group_len):
""" 将文本进行分组处理 """
# 去除文本中的空格
init_message = init_message.replace(" ", "")
# 计算需要补充的字符数(当明文长度不能被分组长度整除时)
blank = len(init_message) % group_len
# 如果有分组长度不足,则补充字符 'z' 直到长度能够整除 group_len
if blank != 0:
init_message = init_message + (group_len - blank) * "z"
# 将字符串转换为小写,并将每个字符转换为对应的 ASCII 码减去 97
init_message_list = list(init_message.lower())
code_list = [ord(i) - 97 for i in init_message_list]
# 将 code_list 按照 group_len 进行分组
code_group = []
for i in range(0, len(code_list), group_len):
code_group.append(code_list[i:i + group_len])
return code_group
def extended_gcd(a, b):
"""扩展欧几里德算法
接受两个整数 a 和 b 作为输入,并返回它们的最大公约数(GCD),以及满足 ax + by = gcd(a, b) 的系数 x 和 y。
"""
if b == 0:
return 1, 0
else:
x, y = extended_gcd(b, a % b)
x, y = y, x - (a // b) * y
return x, y
# 该函数也用于在 modular_inverse_element 函数中找到模反元素。
def modular_inverse_element(a: int, m=26):
"""求整数a关于1模m的乘法逆元"""
# 首先检查 a 和 m 是否互质 若不互质则返回-1
if (np.gcd(a, m) != 1): return -1
# 使用 extended_gcd 函数找到满足 a * inv_a + m * _ = 1 的系数 inv_a 和 _
inv_a, _ = extended_gcd(a, m)
inv_a %= m
return inv_a
def polyalphabetic_substitution(code: list, A, B, m=26) -> list:
"""加密计算函数"""
group_len = len(B) # 获取密钥B的长度作为分组长度
code = np.mat(code).reshape(group_len, 1) # 将code转换为矩阵,并调整形状为(group_len, 1)
C = ((np.dot(A, code)) % m).reshape(-1) # 使用矩阵乘法和取模运算,计算加密后的结果C
# C = ((np.dot(A, code) + B) % m).reshape(-1) # 如果需要加上密钥B,则取消注释此行(但好像有点问题,暂时还没进行修改)
C = C.tolist()[0] # 将结果C转换为列表形式
return C
def modular_inverse_matrix(A, m=26):
"""求矩阵A关于1模m的乘法逆元"""
detA = np.linalg.det(A) # 计算矩阵A的行列式
inv_a = np.linalg.inv(A) # 计算矩阵A的逆矩阵
Adjoint = detA * inv_a # 计算矩阵A的伴随矩阵
inv_detA = modular_inverse_element(round(detA), m) # 计算行列式detA关于1模m的乘法逆元
cipher_inv_a = ((inv_detA % m) * Adjoint) % m # 计算矩阵A关于1模m的乘法逆元
cipher_inv_a = np.round(cipher_inv_a).astype(int) # 将结果四舍五入并转换为整数类型
return cipher_inv_a # 返回矩阵A关于1模m的乘法逆元
def inverse_polyalphabetic_substitution(code: list, inv_a, B, m=26) -> list:
"""解密计算函数"""
group_len = len(B) # 获取密钥B的长度
code = np.mat(code).reshape(group_len, 1) # 将code转换为矩阵,并调整形状为(group_len, 1)
M = (np.dot(inv_a, (code)) % m).reshape(-1) # 解密计算,使用矩阵乘法和取模运算
# M = (np.dot(inv_a, (code - B)) % m).reshape(-1) # 如果需要减去密钥B,则取消注释此行(但好像有点问题,暂时还没进行修改)
M = M.tolist()[0] # 将结果M转换为列表形式
return M
def encrypt_message(key_a, key_b, plaintext):
""" 加密函数 """
ciphertext = ""
code_group = process_text(plaintext, len(key_b))
for group in code_group:
group = polyalphabetic_substitution(group, key_a, key_b)
group = [chr(i + 97) for i in group] # 将数字转换为对应的字母
group = ''.join(group) # 将列表中的字母连接成字符串
ciphertext = ciphertext + group + " "
return ciphertext
def decrypt_message(key_a, key_b, ciphertext):
""" 解密函数 """
plaintext = ""
code_group = process_text(ciphertext, len(key_b))
key_a_inv = modular_inverse_matrix(key_a, m=26)
for group in code_group:
group = inverse_polyalphabetic_substitution(group, key_a_inv, key_b)
group = [chr(i + 97) for i in group] # 将数字转换为对应的字母
group = ''.join(group) # 将列表中的字母连接成字符串
plaintext = plaintext + group + " "
return plaintext
if __name__ == '__main__':
keyA = [[11, 2, 19],
[5, 23, 25],
[20, 7, 17]]
keyB = [0, 0, 0] # 这里B不为0时可能结果有错,暂时还未更改。
plaintext = "your pin no is four one two six"
ciphertext = "wgi fgj tmr lhh xth wbx zps brb"
print("密钥为:", "key_a:", keyA, "\n\t ", "key_b:", keyB)
print("加密为:", encrypt_message(keyA, keyB, plaintext))
print("解密为:", decrypt_message(keyA, keyB, ciphertext))
个人博客:qinquanquan.com