古典密码代码

今天开始上密码课,才想起了之前寒假一时兴起写了古典密码的加密解密python代码,故在此总结下

移位密码

def run():
    print('*'*5 + '移位密码' + '*'*5)
    choice = input('请选择加密/解密(e/d):')
    move = int(input('输入右移位数:'))
    if choice == 'E' or choice == 'e':
        encrypt(move)
    elif choice == 'D' or choice == 'd':
        decrypt(move)
    else:
        print('输入有误')


# 加密
def encrypt(move):
    m = input('请输入明文:')
    print('加密开始……')
    c = ''
    for i in m:    # 遍历明文字符,进行对应操作
        if ord(i) == 32:    # 空格
            c = c + ' '
        elif ord(i) in range(65, 91):    # 大写
            c = c + chr(65 + (ord(i) - 65 + move) % 26)
        elif ord(i) in range(97, 123):    # 小写
            c = c + chr(97 + (ord(i) - 97 + move) % 26)
        else:    # 其他字符
            c = c + i
    print('密文:' + c)


# 解密
def decrypt(move):
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    for i in c:
        if ord(i) == 32:
            m = m + ' '
        elif ord(i) in range(65, 91):
            m = m + chr(65 + (ord(i) - 65 - move) % 26)
        elif ord(i) in range(97, 123):
            m = m + chr(97 + (ord(i) - 97 - move) % 26)
        else:
            m = m + i
    print('明文:' + m)


if __name__ == '__main__':
    run()

凯撒密码

导入移位密码文件,设置move为3即可

import Transposition as yiwei


def run():
    print('*' * 5 + '凯撒密码' + '*' * 5)
    choice = input('请选择加密/解密(e/d):')
    move = 3
    if choice == 'E' or choice == 'e':
        yiwei.encrypt(move)
    elif choice == 'D' or choice == 'd':
        yiwei.decrypt(move)
    else:
        print('输入有误')


if __name__ == '__main__':
    run()

仿射密码

线性变换,需要输入k1、k0

def run():
    print('*' * 5 + '仿射密码' + '*' * 5)
    k1 = int(input('请输入k1:'))
    k0 = int(input('请输入k0:'))
    choice = input('请选择加密/解密(e/d):')
    if choice == 'E' or choice == 'e':
        encrypt(k0, k1)
    elif choice == 'D' or choice == 'd':
        decrypt(k0, k1)
    else:
        print('输入有误')


def encrypt(k0, k1):
    m = input('请输入明文:')
    c = ''
    print('加密开始……')
    for i in m:
        if m.isspace():
            c = c + ' '
        elif i.islower():
            c = c + chr(((ord(i) - 97) * k1 + k0) % 26 + 97)
        elif i.isupper():
            c = c + chr(((ord(i) - 65) * k1 + k0) % 26 + 65)
        else:
            c = c + i
    print('密文:' + c)


def decrypt(k0, k1):
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    inv = 0
    for i in range(26):
        if k1 * i % 26 == 1:
            inv = i
            break
    for i in c:
        if c.isspace():
            m = m + ' '
        elif i.islower():
            m = m + chr(((ord(i) - 97 - k0) * inv) % 26 + 97)
        elif i.isupper():
            m = m + chr(((ord(i) - 65 - k0) * inv) % 26 + 65)
        else:
            m = m + i
    print('明文:' + m)


if __name__ == '__main__':
    run()

密钥短语密码

def run():
    print('*' * 5 + '密钥短语密码(小写)' + '*' * 5)
    choice = input('请选择加密/解密(e/d):')
    key = list(input('输入密钥短语:'))
    keys = create_k(key)
    if choice == 'E' or choice == 'e':
        encrypt(keys)
    elif choice == 'D' or choice == 'd':
        decrypt(keys)
    else:
        print('输入有误')


# 创建密钥列表
def create_k(key):
    keys = []
    for i in key:
        if i not in keys:    # 如果已存在keys列表中,则不添加
            keys.append(i)
    for j in range(97, 123):    # 按a-z顺序添加,除非已存在
        apha = chr(j)
        if apha not in keys:
            keys.append(apha)
    print('Your key:' + ''.join(keys))
    return keys


def encrypt(keys):
    m = input('请输入明文:')
    c = ''
    print('加密开始……')
    for i in m:
        if i.isspace():
            c = c + ' '
        else:
            c = c + keys[ord(i) - 97]    # 将明文字符与新的26字母表,即keys相对应
    print('密文:' + c)

def decrypt(keys):
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    for i in c:
        if i.isspace():
            m = m + ' '
        else:
            m = m + chr(keys.index(i) + 97)
    print('明文:' + m)


if __name__ == '__main__':
    run()
  

维吉尼亚密码

def run():
    print('*' * 5 + '维吉尼亚密码' + '*' * 5)
    choice = input('请选择加密/解密(e/d):')
    k = input('请输入密钥:')
    if choice == 'E' or choice == 'e':
        encrypt(k)
    elif choice == 'D' or choice == 'd':
        decrypt(k)
    else:
        print('输入有误')


def encrypt(k):
    m = input('请输入明文:')
    c = ''
    print('加密开始……')
    for i in range(len(m)):
        if m[i].isspace():
            c = c + ' '
        elif m[i].islower():
            c = c + chr((ord(m[i]) - 97 + ord(k[i % len(k)]) - 97) % 26 + 97)
        elif m[i].isupper():
            c = c + chr((ord(m[i]) - 65 + ord(k[i % len(k)]) - 65) % 26 + 65)
        else:
            c = c + m[i]
    print('密文:' + c)


def decrypt(k):
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    for i in range(len(c)):
        if c[i].isspace():
            m = m + ' '
        elif c[i].islower():
            m = m + chr((ord(c[i]) - ord(k[i % len(k)])) % 26 + 97)
        elif c[i].isupper():
            m = m + chr((ord(c[i]) - ord(k[i % len(k)])) % 26 + 65)
    print('明文:' + m)


if __name__ == '__main__':
    run()
  

希尔密码

这部分比较难写代码,在网上搜集了很多学习资料,最后才发现比较符合自己想法的文章https://hstar.me/2020/08/hill-cipher-study/

算法如图:

image-20220224214234407.png

然后稍微复习了下线性代数:https://www.shuxuele.com/algebra/matrix-inverse-minors-cofactors-adjugate.html

但是这里的算法并不是简单的求矩阵逆,还要与替换表的长度26进行相应的模运算,网上很多都是简单的求逆矩阵

快速求模乘法逆:https://www.delftstack.com/zh/howto/python/mod-inverse-python/,使用pow()内置函数:pow(a,-1,m),a为要找到模逆的数,m为模数

还有个比较关键的细节:关于python的取整函数

  • int:去除小数部分,即只保留前面的整数,向0取整
  • round:遵循四舍五入原则,本例中应使用该法
import numpy as np    # 与矩阵计算相关的库


def run():
    print('*' * 5 + '希尔密码' + '*' * 5)
    choice = input('请选择加密/解密(e/d):')
    if choice == 'E' or choice == 'e':
        encrypt()
    elif choice == 'D' or choice == 'd':
        decrypt()
    else:
        print('输入有误')


# 创建密钥矩阵
def create_k():
    print('**输入密钥模式**')
    print('1:数字密钥;2:字母密钥')
    choice = int(input('请选择模式(数字):'))
    n = int(input('请输入密钥矩阵的行数:'))
    k_list = []
    print('请输入' + str(n) + '*' + str(n) + '可逆矩阵(每行' + str(n) + '个,空格隔开):')
    for i in range(1, n + 1):
        while True:
            row = input(f'第{i}行:')
            row_list = row.split(' ')
            if choice == 1:
                row_list = [int(j) for j in row_list]
            elif choice == 2:
                row_list = [ord(j) - 97 for j in row_list]
            # 判断矩阵每行个数是否正确
            if len(row_list) != n:
                print('输入个数错误,请重新输入:')
            else:
                break
        k_list.append(row_list)
    # print(k_list)
    if if_inv_matrix(k_list):
        return np.array(k_list)
    else:
        print('不存在逆矩阵!')


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


def encrypt():
    m = input('请输入明文:')
    print('加密开始……')
    c = ''
    m_list = []
    for i in m:
        m_list.append(ord(i) - 97)
    k = create_k()
    # 检查并补齐明文,使之满足n维向量与n*n矩阵相乘
    row_num = len(k)
    add_m_num = (row_num - len(m_list) % row_num) % row_num
    if add_m_num != 0:
        for n in range(add_m_num):
            m_list.append(0)
        print(f'添加了{add_m_num}个0补齐明文')
    # 分组乘积
    for j in range(int(len(m_list) / row_num)):
        # 线性代数乘积
        M = m_list[j * row_num: (j + 1) * row_num]
        c_list = np.matmul(k, M)
        c = c + ''.join([chr(x % 26 + 97) for x in c_list])
    # print(c[:len(c) - add_m_num])
    print('密文:' + c)


# 求最大公因数
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)


def decrypt():
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    c_list = []
    for i in c:
        c_list.append(ord(i) - 97)
    k = create_k()
    row_num = len(k)
    det_k = np.linalg.det(k)    # 获取密钥矩阵的行列式
    inv_det_k = pow(int(det_k), -1, 26)    # 求密钥矩阵行列式模逆
    if gcd(inv_det_k, 26) != 1:
        print('密钥矩阵行列式与26不互质,无法解密!')
        return
    elif inv_det_k == 0:
        print('密钥矩阵行列式不能为0!')
        return
    inv_k = inv_det_k * np.linalg.inv(k) * det_k % 26 # 求希尔密码密钥矩阵的逆矩阵(同上公式)
    for j in range(int(len(c_list) / row_num)):
        # 线性代数乘积
        C = np.array(c_list[j * row_num: (j + 1) * row_num])
        # print(C)
        m_list = np.matmul(inv_k, C)
        # 这里一定要注意不要用int来取整,他这里省略了小数部分,应该用近似round,血泪教训!
        m = m + ''.join([chr(round(x) % 26 + 97) for x in m_list])
    print('明文:' + m + '(由于可能存在补齐的明文,请自行去掉末尾可能多余的a)')


if __name__ == '__main__':
    run()

Playfair密码

这个是真复杂,难想,一直算出来的与在线网站加密解密不一样,最后才发现原来有两种主流方式

  • 在重复数字后加‘x’
  • 第二个重复数字改成‘x’

第一种是我的法和普莱费尔密码加密/解密 - 一个工具箱 - 好用的在线工具都在这里! (atoolbox.net)这个网址的方法

第二种是pycipher库下的Playfair的方法和CTF在线工具-在线普莱菲尔密码加密|在线普莱菲尔密码解密|普莱菲尔密码算法|Playfair Cipher (hiencode.com)普莱菲尔密码 - Playfair Cipher - 在线工具网 (wtool.com.cn)等的方法

最后验证出自己结果正确时真高兴,shit,手算了老多遍了

def run():
    print('*' * 5 + 'Playfair密码' + '*' * 5)
    choice = input('请选择加密/解密(e/d):')
    if choice == 'E' or choice == 'e':
        encrypt()
    elif choice == 'D' or choice == 'd':
        decrypt()
    else:
        print('输入有误')


# 初始化5*5矩阵
def create_k():
    key = input('请输入密钥:')
    key = key.replace(' ', '')
    keys = []   # 创建密钥列表
    for i in key:
        if i not in keys:
            if i == 'j':    #
                keys.append('i')
            else:
                keys.append(i)
    for j in range(97, 123):
        apha = chr(j)
        if apha not in keys:
            if apha == 'j':
                if 'i' not in keys:
                    keys.append('i')
                else:
                    continue
            else:
                keys.append(apha)
    # 创建密钥矩阵
    # 初始化5*5矩阵,这里构建不能采用[[0]*5]*5,否则后面赋值错误,会变换整列
    k_arr = [[0] * 5 for i in range(5)]
    k = 0
    print('\t**密钥矩阵**')
    for i in range(5):
        for j in range(5):
            k_arr[i][j] = keys[k]
            print(keys[k], end='\t')
            if (k + 1) % 5 == 0:
                print('')
            k += 1
    return k_arr


# 获取字符对应矩阵的位置
def get_index(p, k_arr):
    if p == 'j':
        p = 'i'
    for i in range(len(k_arr)):
        for j in range(len(k_arr)):
            if k_arr[i][j] == p:
                return i, j


def encrypt():
    m = input('请输入明文:')
    m = m.replace(' ', '')  # 去除空格
    print('加密开始……')
    c = ''
    c_list = []
    k_arr = create_k()
    for i in range(0, len(m) + 1, 2):
        if i < len(m) - 1:
            if m[i] == m[i + 1]:
                # m = m[:i + 1] + 'x' + m[i + 2:] # 第二个重复字母改成‘x’
                m = m[:i + 1] + 'x' + m[i + 1:]    # 在重复字母后加‘x’
    if len(m) % 2 != 0:
        m = m + 'x'
    print('整理后的明文:' + m)
    for j in range(0, len(m), 2):
        P1 = get_index(m[j], k_arr)
        P2 = get_index(m[j+1], k_arr)
        print(P1, P2)
        if P1[0] == P2[0]:  # P1、P2在同一行
            C1 = k_arr[P1[0]][(P1[1] + 1) % 5]
            C2 = k_arr[P2[0]][(P2[1] + 1) % 5]
        elif P1[1] == P2[1]:    # P1、P2在同一列
            C1 = k_arr[(P1[0] + 1) % 5][P1[1]]
            C2 = k_arr[(P2[0] + 1) % 5][P2[1]]
        else:
            C1 = k_arr[P1[0]][P2[1]]
            C2 = k_arr[P2[0]][P1[1]]
        c_list.append(C1 + C2)
    c = ''.join(c_list)
    print('密文:' + c)


def decrypt():
    c = input('请输入密文:')
    m = ''
    print('解密开始……')
    m_list = []
    k_arr = create_k()
    for j in range(0, len(c), 2):
        C1 = get_index(c[j], k_arr)
        C2 = get_index(c[j+1], k_arr)
        if C1[0] == C2[0]:  # C1、C2在同一行
            P1 = k_arr[C1[0]][(C1[1] - 1) % 5]
            P2 = k_arr[C2[0]][(C2[1] - 1) % 5]
        elif C1[1] == C2[1]:    # C1、C2在同一列
            P1 = k_arr[(C1[0] - 1) % 5][C1[1]]
            P2 = k_arr[(C2[0] - 1) % 5][C2[1]]
        else:
            P1 = k_arr[C1[0]][C2[1]]
            P2 = k_arr[C2[0]][C1[1]]
        m_list.append(P1 + P2)
    m = ''.join(m_list)
    # 删除添加的x,但是无法准确判断结尾x是否存在明文中,需要结合语义进行判断
    for i in range(len(m)):
        if 0 < i < len(m) - 1 and m[i] == 'x' and m[i - 1] == m[i + 1]:
            m = m[:i] + m[i+1:]
    print('明文:' + m + '(若结尾存在x,请自行判断x是否需要存在)')


if __name__ == '__main__':
    run()
  

总代码:

import Transposition as yiwei
import Caesar
import Affine
import KeyPhrase as kp
import Vigenere as vig
import Hill
import Playfair as pf

def run():
    while True:
        print('*'*10 + 'Powered By Xherlock' + '*'*10)
        print(' ' * 10 + '1.移位密码')
        print(' ' * 10 + '2.凯撒密码')
        print(' ' * 10 + '3.仿射密码')
        print(' ' * 10 + '4.密钥短语密码')
        print(' ' * 10 + '5.维吉尼亚密码')
        print(' ' * 10 + '6.希尔密码')
        print(' ' * 10 + '7.Playfair密码')
        choice = int(input('请选择密码种类:'))
        if choice == 1:
            yiwei.run()
        elif choice == 2:
            Caesar.run()
        elif choice == 3:
            Affine.run()
        elif choice == 4:
            kp.run()
        elif choice == 5:
            vig.run()
        elif choice == 6:
            Hill.run()
        elif choice == 7:
            pf.run()
        print('\n')


if __name__ == '__main__':
    run()
最后修改:2022 年 02 月 24 日
如果觉得我的文章对你有用,请随意赞赏