目录

  1. 1. 前言
  2. 2. 单表代换密码
    1. 2.1. 凯撒密码
      1. 2.1.1. 数学原理
      2. 2.1.2. 例子
      3. 2.1.3. 解密脚本
    2. 2.2. 移位变换
    3. 2.3. 仿射变换
  3. 3. 多表代换
    1. 3.1. Hill
    2. 3.2. Vigenere
      1. 3.2.1. 加密
      2. 3.2.2. 解密
      3. 3.2.3. 脚本

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

古典密码

2023/5/18 Crypto
  |     |   总文章阅读量:

前言

记录一下古典密码

单表代换密码

凯撒密码

一种替换密码,即将明文中的每个字母都替换为字母表中固定数量的字母

例如,如果将明文中的每个字母向右移动3个位置,那么字母”A”将被替换为字母”D”,字母”B”将被替换为字母”E”,以此类推。这个固定的移动量被称为密钥或偏移量。

数学原理

以上面那个例子为例,代换如下:

c = E3(m) ≡ m+3(mod 26),0 ≤ m ≤ 25

m = D3(c) ≡ c-3(mod 26),0 ≤ c ≤ 25

例子

假设我们要加密明文”HELLO”并使用偏移量3。首先,我们将明文中的每个字母向右移动3个位置。这将产生密文”KHOOR”。解密就是将密文中的每个字母向左移动3个位置,即将偏移量取反,以还原原始明文。

解密脚本

def caesar_decrypt(ciphertext, shift):
    """
    凯撒密码解密函数
    :param ciphertext: 密文
    :param shift: 移位数
    :return: 明文
    """
    plaintext = ""
    for char in ciphertext:
        if char.isalpha():
            # 将字母转换为ASCII码,并减去65或97,使得A或a的ASCII码为0
            ascii_code = ord(char) - shift
            # 处理超出字母表范围的ASCII码
            if char.isupper():
                if ascii_code < 65:
                    ascii_code += 26
            else:
                if ascii_code < 97:
                    ascii_code += 26
            # 将ASCII码转换回字符
            plaintext += chr(ascii_code)
        else:
            plaintext += char
    return plaintext


ciphertext = ""
# ciphertext = ''.join([chr(i) for i in [86, 116, 128, 80, 98, 85, 139, 122, 134, 114, 125, 136, 117, 123, 129, 127, 128, 128, 142, 130, 140, 147, 127, 132, 131, 136, 151, 134, 152, 164]])
for i in range(1, 30):
    shift = i
    plaintext = caesar_decrypt(ciphertext, shift)
    print(plaintext)

移位变换

和凯撒密码相比,移位变换就是把移动的位数改成了变量 k

即:

c = Ek(m) ≡ m+k(mod 26),0 ≤ m ≤ 25

m = Dk(c) ≡ c-k(mod 26),0 ≤ c ≤ 25

对应的解密脚本:

def decrypt(enc, shift):
    dec = ""
    for char in enc:
        temp = ord(char) - shift
        if char.isupper():
            if temp < 65:
                temp += 26
        else:
            if temp < 97:
                temp += 26
        dec += chr(temp)
    return dec

enc = "BEEAKFYDJXUQYHYJIQRYHTYJIQFBQDUYJIIKFUHCQD"
for i in range(1, 25):
    key = i
    flag = decrypt(enc, key)
    print(flag + " " + str(key))

仿射变换

在前面的基础上又在 m 前多了个倍数a,此时解密就要用到a的逆元了

c = Ea,b(m) ≡ am+b(mod 26),0 ≤ m ≤ 25

m = Da,b(c) ≡ a-1(c-b) (mod 26),0 ≤ c ≤ 25

则解密的流程为:

  1. 求a的逆元,python的Crypto.Util.number库已经帮我们实现好了,直接调用即可
  2. 遍历字符串,每一位的ascii值与A的ascii值之差(假设全为大写)再减去偏移量b,最后的结果再乘a的逆元,模26,得到对应的字母在字母表的序号(比如说 0 对应 A,1 对应 B 这样)
  3. 序号加上A的ascii值,再把每个得到的字符拼接在一起即可

以书上的第二题为例,对应的解密代码:

from Crypto.Util.number import *
def decrypt(enc,a,b):
    dec = ""
    re_a=inverse(a,26)
    for char in enc:
        temp=(ord(char)-ord("A")-b)*re_a %26
        dec+=chr(temp+ord("A"))
    return dec
prefix = "IF"
enc = "EDSGICKXHUKLZVEQZVKXWKZUKVCUH"
def cal():
    for a in range(26):
        for b in range(26):
            m1 = ord('I') - ord('A')
            m2 = ord('F') - ord('A')
            c1 = ord('E') - ord('A')
            c2 = ord('D') - ord('A')
            if ((a*m1 + b) % 26 == c1) and ((a*m2 + b)%26 == c2):
                return a,b
a,b = cal()
print(decrypt(enc,a,b))

多表代换

Hill

希尔密码(Hill)使用每个字母在字母表中的顺序作为其对应的数字,即 A=0,B=1,C=2 等,然后将明文转化为 n 维向量,跟一个 n × n 的矩阵相乘,再将得出的结果模 26

注意用作加密的矩阵(即密匙)必须是可逆的

在线工具:www.practicalcryptography.com/ciphers/hill-cipher/

对应的数学公式:

Ci ≡ AMi + B(mod M),i = 1,2, … ,j

计算流程:

image-20240428223234326

后面几位的求密文的步骤也是一样的

解密时,只需要求A的逆矩阵再和每一组矩阵相乘即可得到明文


Vigenere

维吉尼亚密码(Vigenère cipher)是一种多表代换密码,也称为多重代换密码

维吉尼亚密码的加密过程是将明文按照关键词(密钥)重复排列,然后用不同的凯撒密码表对每个字符进行加密。每个凯撒密码表是由26个字母构成的,其中每个字母按照固定的偏移量进行置换。关键词的长度可以与明文的长度不同。解密时,需要使用相同的关键词和偏移量,依次对密文中的每个字符进行解密。

加密

流程:以下面这个为例

明文:come greatwall
密钥:crypto
  1. 首先,对密钥进行填充使其长度与明文长度一样

    明文 c o m e g r e a t w a l l
    密钥 c r y p t o c r y p t o c
  2. 接下来查表得到密文

    image-20240428224051375

最终得到密文:

明文:come greatwall
密钥:crypto
密文:efkt zferrltzn

解密

破译维吉尼亚密码的关键在于它的密钥是循环重复的

基于类似 the 这样的常用单词有可能被同样的密钥字母进行加密,从而在密文中重复出现

例如,明文中不同的CRYPTO可能被密钥ABCDEF加密成不同的密文:

密钥:ABCDEF AB CDEFA BCD EFABCDEFABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASXT IT UKSWT GQU GWYQVRKWAQJB

此时明文中重复的元素在密文中并不重复。然而,如果密钥相同的话,结果可能便为(使用密钥ABCD):

密钥:ABCDAB CD ABCDA BCD ABCDABCDABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASTP KV SIQUT GQU CSASTPIUAQJB

那么我们就可以爆破最终得到密钥长度

在线解密网站:Vigenere Solver | guballa.de

脚本

来源于nss上某位大佬的脚本

letter_list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'  # 字母表


# 根据输入的key生成key列表
def Get_KeyList(key):
    key_list = []
    for ch in key:
        key_list.append(ord(ch.upper()) - 65)
    return key_list


# 加密函数
def Encrypt(plaintext, key_list):
    ciphertext = ""

    i = 0
    for ch in plaintext:  # 遍历明文
        if 0 == i % len(key_list):
            i = 0
        if ch.isalpha():  # 明文是否为字母,如果是,则判断大小写,分别进行加密
            if ch.isupper():
                ciphertext += letter_list[(ord(ch) - 65 + key_list[i]) % 26]
                i += 1
            else:
                ciphertext += letter_list[(ord(ch) - 97 + key_list[i]) %
                                          26].lower()
                i += 1
        else:  # 如果密文不为字母,直接添加到密文字符串里
            ciphertext += ch
    return ciphertext


# 解密函数
def Decrypt(ciphertext, key):
    plaintext = ""

    i = 0
    for ch in ciphertext:  # 遍历密文
        if 0 == i % len(key_list):
            i = 0
        if ch.isalpha():  # 密文为否为字母,如果是,则判断大小写,分别进行解密
            if ch.isupper():
                plaintext += letter_list[(ord(ch) - 65 - key_list[i]) % 26]
                i += 1
            else:
                plaintext += letter_list[(ord(ch) - 97 - key_list[i]) %
                                         26].lower()
                i += 1
        else:  # 如果密文不为字母,直接添加到明文字符串里
            plaintext += ch
    return plaintext


if __name__ == '__main__':
    print("加密请按D,解密请按E:")
    user_input = input()
    while (user_input != 'D' and user_input != 'E'):  # 输入合法性判断
        print("输入有误!请重新输入:")
        user_input = input()

    print("请输入密钥,随便输后面会爆破,大写字母就行:")
    key = input()
    while (False == key.isalpha()):  # 输入合法性判断
        print("输入有误!密钥为字母,请重新输入:")
        key = input()

    key_list = Get_KeyList(key)
    # print(key_list)
    if user_input == 'D':
        # 加密
        print("请输入明文:")
        plaintext = input()
        ciphertext = Encrypt(plaintext, key_list)
        print("密文为:\n%s" % ciphertext)
    else:
        # 解密
        print(key_list)
        print("请输入密文,把密文输进去:")
        ciphertext = input()
        for i in range(26):
            for j in range(26):
                for k in range(26):
                    key_list = [i, j, k]
                    plaintext = Decrypt(ciphertext, key_list)
                    if "NSS" in plaintext:
                        print(plaintext)
                        break
                    # print("明文为:\n%s" % plaintext)