古典密码(二)

【乘法密码(Multiplication Cipher)】

  乘法密码也是一种简单的替代密码,与凯撒密码相似,凯撒密码用的是加法,而乘法密码用的自然是乘法。这种方法形成的加密信息保密性比较低。

  加密公式:密文 = (明文 * 乘数) Mod 26

  对于乘数密码,只有当乘数与26互质时,加密之后才会有唯一的解,因此乘数只可能有如下11种的选择:

  乘数 = 3,5,7,9,11,15,17,19,21,23,25

【希尔密码(Hill Cipher)】

  希尔密码就是矩阵乘法密码,运用基本矩阵论原理的替换密码。每个字母当作26进制数字:A=0, B=1, C=2... 一串字母当成n维向量,跟一个n×n的密钥矩阵相乘,再将得出的结果模26。希尔密码的优点是完全隐藏了字符的频率信息,弱点是容易被已知明文攻击击破。

加密
  例如:密钥矩阵
1 3
0 2

  明文:HI THERE

  去空格,2个字母一组,根据字母表顺序换成矩阵数值如下,末尾的E为填充字元:

HI TH ER EE
8 20 5 5
9 8 18 5

HI 经过矩阵运算转换为 IS,具体算法参考下面的说明:

|1 3| 8 e1*8+3*9=35 MOD26=9 =I
|0 2| 9 e0*8+2*9=18 MOD26=18=S

  用同样的方法把“HI THERE”转换为密文“IS RPGJTJ”,注意明文中的两个E分别变为密文中的G和T。

解密
  解密时,必须先算出密钥的逆矩阵,然后再根据加密的过程做逆运算。

  逆矩阵算法公式:
|A B| = 1/(AD-BC) * | D -B|
|C D| |-C A|

例如密钥矩阵=
|1 7|
|0 3|
AD-BC=1*3-0*7=3 3*X=1 mod26 所以 X=9
  因此
|1 7| 的逆矩阵为: 9 * |3 -7|
|0 3| |0 1|

  假设密文为“FOAOESWO”

FO AO ES WO
6 1 5 23
15 15 19 15

9* |3 -7| | 6| = 9*(3*6-7*15)=-783 mod26 = 23=W
|0 1| |15| = 9*(0*6+1*15)= 135 mod26 = 5 =E
所以密文“FOAOESWO”的明文为“WEREDONE”

import numpy as np
import sys


# 判断矩阵是否存在逆矩阵
def judge_inverse_matrix(matrix):
    try:
        np.linalg.inv(matrix)
    except:
        return False
    return True


# 输入列表并转换为矩阵
def inputmatrix():
    row_num = int(input("请输入矩阵的行数:"))
    all_list = []
    for i in range(1, row_num + 1):
        row = input(f"请输入加密矩阵第{i}行(以空格为分隔):")
        if row[0] == ' ':
            print("输入有误,第一位不该为空格")
            sys.exit()
        else:
            row_list = row.split(' ')
        # 将列表中str转换为int
        if len(row_list) == row_num:
            for n in row_list:
                row_list[row_list.index(n)] = int(row_list[row_list.index(n)])
            all_list.append(row_list)
        else:
            print("前后输入的行数不一致,请重修输入")
            break
    encrypt_matrix = np.array(all_list)
    if not judge_inverse_matrix(encrypt_matrix):
        print("该矩阵不存在逆矩阵,请重修输入")
    return encrypt_matrix


# 生成矩阵的逆矩阵。如果逆矩阵含有小数,就四舍五入
def generate_inverse_matrix(matrix):
    inverse_matrix = np.linalg.inv(matrix)
    for row in inverse_matrix:
        for num in row:
            num = round(num)
    print("加密矩阵的逆矩阵为:")
    for array in inverse_matrix:
        print(array)
    return inverse_matrix


# 生成字母-数字对应的字典
def alphabet_number():
    alphabet_number_dict = {}
    for i in range(97, 123):
        alphabet_number_dict[chr(i)] = i % 97
    return alphabet_number_dict


def encrypt():
    # 明文字母转换成对应数字
    input_plaintext = input("请输入明文:")
    num_list = []
    dic = alphabet_number()
    for i in input_plaintext:
        num_list.append(dic[i])

    # 如果矩阵行数不能整除明文,则用'z'的数字25补全
    matrix = inputmatrix()
    row_num = len(matrix)
    supple_num = row_num - (len(num_list) % row_num)
    if len(num_list) % row_num != 0:
        for n in range(1, supple_num + 1):
            num_list.append(25)
    print(f"\n添加了{supple_num}个z补全明文")

    # 分组加密
    group_num = int(len(num_list) / row_num)
    whole_encrypt_num_list = []
    for g in range(0, group_num):
        plaintext_matrix = np.array(num_list[0 + g * row_num: (g + 1) * row_num])
        encrypt_num_list = np.matmul(plaintext_matrix, matrix)
        for num in encrypt_num_list:
            whole_encrypt_num_list.append(num)

    # 将加密后的数字转换为字母
    ciphertext = ""
    for ennum in whole_encrypt_num_list:
        # 对超出范围的数字取模
        if ennum > 25:
            ennum = ennum % 26
        for k in dic:
            if dic[k] == ennum:
                ciphertext = ciphertext + k
    print("加密后密文为:", ciphertext, '\n')


def decrypt():
    # 输入密文并转换为对应数字
    input_ciphertext = input("请输入密文:")
    num_list2 = []
    dic2 = alphabet_number()
    for i in input_ciphertext:
        num_list2.append(dic2[i])

    # 解密就不添加'z'来补全密文了
    matrix = inputmatrix()
    row_num2 = len(matrix)
    supple_num2 = row_num2 - (len(num_list2) % row_num2)

    # 用逆矩阵分组解密
    inserve_matrix = generate_inverse_matrix(matrix)
    group_num2 = int(len(num_list2) / row_num2)
    whole_decrypt_num_list = []
    for g in range(0, group_num2):
        plaintext_matrix = np.array(num_list2[0 + g * row_num2: (g + 1) * row_num2])
        decrypt_num_list = np.matmul(plaintext_matrix, inserve_matrix)
        for num in decrypt_num_list:
            whole_decrypt_num_list.append(num)

    # 将解密后的数字转换为对应字母
    plaintext = ""
    for denum in whole_decrypt_num_list:
        if denum > 25 or denum < -26:
            denum = denum % 26

        # 防止取模后是负数,字典中找不到对应的字母
        if denum < 0:
            denum = denum + 26
        # 字典中寻找与数字对应的字母
        for k in dic2:
            if dic2[k] == denum:
                plaintext = plaintext + k
    print("解密后明文为:", plaintext, '\n')


if __name__ == '__main__':
    while True:
        print("========Hill密码========\n")
        print("1.加密\n2.解密\n")
        print("注意:如果输入矩阵的逆矩阵中含有小数,采用四舍五入的方法\n")
        pattern = input("请选择模式:")
        if pattern == '1':
            encrypt()
        elif pattern == '2':
            decrypt()
        else:
            print("输入有误,请重修输入")

【仿射密码】

仿射密码为单表加密的一种,字母系统中所有字母都藉一简单数学方程加密,对应至数值,或转回字母。它是一种替换密码,利用加密函数一个字母对一个字母的加密.
密钥空间
字符集中 m 往往是 26
gcd(a,m)=1, 所以 a 取值空间大小为 φ(m)=12,
b∈[0,26)
密钥空间是 26×12=312 故在已知部分明文的情况下才可以攻击!
加密函数
加密函数是 E(x)= (ax + b) (mod m),其中,a和m互质,m是字符集的大小。

(例如,26即是以26个字母作为编码,当m是26时,a必须是1,3,5,7,9,11,15,17,19,21,23,25其中之一)

解密函数
解密函数为D(x) = a^-1(x - b) (mod m),其中a-1是a在Zm群的乘法逆元。
很容易通过加密函数来推导:
a⋅x+b ≡ y mod m\na⋅x ≡ (y−b) mod m
x ≡ a^−1 ⋅(y−b) mod m
乘法逆元
群G中任意一个元素a,都在G中有唯一的逆元a`,具有性质aa` = a`a = e,其中e为群的单位元。
例如:7 * 15 = 1 mod 26\n15就是7的乘法逆元

'''
仿射密码K = (a,b)
加密函数是E(x)= (ax + b) (mod 26)
解密函数为D(x) = (a^-1)(x - b) (mod 26),其中a^-1是a的乘法逆元
'''


# 加密
def enc(a, b, e):
    c = []
    for i in e:
        temp = ((ord(i) - 97) * a + b) % 26 + 97 # a的ascii码是97
        c.append(chr(temp))
    print(''.join(c).upper())


# 遍历得到a的乘法逆元
def get_multiplicative_inverse(a):
    for i in range(1, 27):
        if a * i % 26 == 1:
            return i


# 解密
def dec(a, b, d):
    a_mul_inv = get_multiplicative_inverse(a)
    p = []
    for i in d:
        temp = (((ord(i) - 97) - b) * a_mul_inv) % 26 + 97
        p.append(chr(temp))
    print(''.join(p).upper())


if __name__ == "__main__":
    a, b = 7, 3
    e = 'hot'
#     d = 'axg'
    enc(a, b, e)
#     dec(a, b, d)

【埃特巴什码(Atbash Cipher)】

Atbash密码是一种替换密码,具有特定的密钥,其中字母表中的字母是颠倒的
例如,所有的“A”被替换为“Z”,所有的“B”被替换为“Y”,以此类推
它最初用于希伯来字母表,但可以用于任何字母表。

key = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
choise = input("0:加密,1:解密")
key.split()
if choise == 0:
    plain = input("please input plaintext:>")
    plain.split()
    for i in range(len(plain)):
        for x in range(26):
            if plain[i] == key[x]:
                plain[i] = key[25-x]
    print(plain)
else:
    ciphertext = input("please input ciphertext:>")
    ciphertext.split()
    for i in range(len(ciphertext)):
        for x in range(26):
            if ciphertext[i] == key[x]:
                ciphertext[i] = key[25-x]
    print(ciphertext)

【AutokeyCipher】
其又称自动密钥密码,与维吉尼亚密码类似,但它是使用不同的方法生成密钥。所以比维吉尼亚密码更安全,自动密钥密码主要有两种:关键词自动密钥密码和原文自动密钥密码。以下是关键词自动密钥密码:
明文:THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
关键词:CULTURE
自动生成密钥:CULTURE THE QUICK BROWN FOX JUMPS OVER THE
接下来的加密过程和维吉尼亚密码类似,从相应的表格可得(这表格我们应该是不知道的,因为关键词不定):
密文:VBP JOZGD IVEQV HYY AIICX CSNL FWW ZVDP WVK

from pycipher import Autokey
 
print (Autokey('CULTURE').encipher('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG'))    #加密
print (Autokey('CULTURE').decipher('VBP JOZGD IVEQV HYY AIICX CSNL FWW ZVDP WVK'))    #解密

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值