多表代换密码原理 + python代码

多表代换密码原理 + 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 CiAMi+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 MiA1(CiB)(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} A1= 115202237192517 1= 10155239972217
再求: M 1 \mathbf{M}_1 M1 = A − 1 \mathbf{A}^{-1} A1 ( 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

  • 44
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qinquanquan_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值