密码学实验

 以实验报告的形式,采用python语言实现密码学里的经典实验

目录

一、古典密码

1.移位密码

2.仿射密码

3.维吉尼亚密码

4.维吉尼亚密码分析

二、分组密码

1.DES加密算法

2.AES加密算法

3.SM4加密算法

三、序列密码

四、公钥密码


一、古典密码

1.移位密码

(1)设计思路

移位密码是将明文的每一个字母用它后面第k个字母代替,属于单表代替的一种,单表代替是加解密使用一个固定的替换表。

加密函数:E_k(m)=(m+k)\mod 26 = c

解密函数:D_k(c)=(c-k)\mod 26=m

(2)实现代码

# 加密
def shift_cipher_encrypt(plaintext, key):
    ciphertext = ""
    for char in plaintext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行加密
            if (char.isupper()):
                ciphertext += chr((ord(char) + key - 65) % 26 + 65)
            else:
                ciphertext += chr((ord(char) + key - 97) % 26 + 97)
        else:
            ciphertext += char
    return ciphertext


# 解密
def shift_cipher_decrypt(ciphertext, key):
    plaintext = ""
    for char in ciphertext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行解密
            if (char.isupper()):
                plaintext += chr((ord(char) - key - 65) % 26 + 65)
            else:
                plaintext += chr((ord(char) - key - 97) % 26 + 97)
        else:
            plaintext += char
    return plaintext


# 主函数
def main():
    while True:
        x = input("请选择模式(1.加密 2.解密 3.退出):")
        if x == "1":
            print("-------------加密过程-------------")
            plaintext = input("请输入明文: ")
            key = int(input("请输入密钥: "))
            print("加密结果: ", shift_cipher_encrypt(plaintext, key))
            print("--------------------------------")
        elif x == "2":
            print("-------------解密过程-------------")
            ciphertext = input("请输入密文: ")
            key = int(input("请输入密钥: "))
            print("解密结果: ", shift_cipher_decrypt(ciphertext, key))
            print("--------------------------------")
        else:
            break


if __name__ == '__main__':
    main()

(3)实验结果

使用移位密码加密:

使用移位密码解密:

恢复用移位密码加密的片段:

        题目:在Alice和Bob的保密通信中,传送的密文是“rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs”,如果他们使用的是移位密码算法,试解密其通信内容

        思路:密钥遍历(0~25),观察解密得到的结果

for key in range(26):
    c = "rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs"
    m = shift_cipher_decrypt(c,key)
    print(m,"对应密钥为",key)

 可以看到当密钥为5时解密得到的结果是有意义的。

(4)频率分析法

移位密码常用的分析方法是频率分析法,利用字母出现频率的统计特性,在没有密钥的情况下推测出密钥并解密。

使用频率分析法对密文“rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs”进行解密,结果如下

 具体代码:通过统计频率并利用卡方检验来猜测移位密码的密钥

from collections import Counter
import string


# 加密
def shift_cipher_encrypt(plaintext, key):
    ciphertext = ""
    for char in plaintext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行加密
            if (char.isupper()):
                ciphertext += chr((ord(char) + key - 65) % 26 + 65)
            else:
                ciphertext += chr((ord(char) + key - 97) % 26 + 97)
        else:
            ciphertext += char
    return ciphertext


# 解密
def shift_cipher_decrypt(ciphertext, key):
    plaintext = ""
    for char in ciphertext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行解密
            if (char.isupper()):
                plaintext += chr((ord(char) - key - 65) % 26 + 65)
            else:
                plaintext += chr((ord(char) - key - 97) % 26 + 97)
        else:
            plaintext += char
    return plaintext

# 频率分析
# 标准英文文本的字母频率分布
ENGLISH_FREQUENCY = {
    'a': 0.08167, 'b': 0.01492, 'c': 0.02782, 'd': 0.04253, 'e': 0.12702,
    'f': 0.02228, 'g': 0.02015, 'h': 0.06094, 'i': 0.06966, 'j': 0.00153,
    'k': 0.00772, 'l': 0.04025, 'm': 0.02406, 'n': 0.06749, 'o': 0.07507,
    'p': 0.01929, 'q': 0.00095, 'r': 0.05987, 's': 0.06327, 't': 0.09056,
    'u': 0.02758, 'v': 0.00978, 'w': 0.02360, 'x': 0.00150, 'y': 0.01974,
    'z': 0.00074
}

# 计算文本中每个字母的出现频率
def calculate_frequency(text):
    # 将输入的文本全部转换为小写,以确保大小写不影响统计
    text = text.lower()
    letter_counts = Counter(char for char in text if char in string.ascii_lowercase)
    total_count = sum(letter_counts.values())
    return {char: count / total_count for char, count in letter_counts.items()}

# 计算观测到的字母频率与预期的字母频率之间的卡方统计量
def chi_squared_stat(observed, expected):
    return sum((observed.get(char, 0) - expected[char]) ** 2 / expected[char] for char in expected)

# 对密文进行频率分析,尝试找出最可能的密钥
def frequency_analysis(ciphertext):
    ciphertext = ciphertext.lower()
    best_key = None
    best_chi_squared = float('inf')

    # 尝试所有可能的密钥(0到25)
    for key in range(26):
        decrypted_text = ''.join(
            chr((ord(char) - ord('a') - key) % 26 + ord('a')) if char in string.ascii_lowercase else char
            for char in ciphertext
        )# 使用当前密钥对密文进行解密。对于每个字母字符,计算其位移后的新字符;对于非字母字符,保持原样。
        observed_freq = calculate_frequency(decrypted_text)
        chi_squared = chi_squared_stat(observed_freq, ENGLISH_FREQUENCY)

        if chi_squared < best_chi_squared:
            best_chi_squared = chi_squared
            best_key = key

    return best_key

# 主函数
def main():
    while True:
        x = input("请选择模式(1.加密 2.解密 3.频率分析 4.退出):")
        if x == "1":
            print("-------------加密过程-------------")
            plaintext = input("请输入明文: ")
            key = int(input("请输入密钥: "))
            print("加密结果: ", shift_cipher_encrypt(plaintext, key))
            print("--------------------------------")
        elif x == "2":
            print("-------------解密过程-------------")
            ciphertext = input("请输入密文: ")
            key = int(input("请输入密钥: "))
            print("解密结果: ", shift_cipher_decrypt(ciphertext, key))
            print("--------------------------------")
        elif x == "3":
            print("-------------频率分析过程-------------")
            ciphertext = input("请输入密文: ")
            best_key = frequency_analysis(ciphertext)
            print("推测的密钥: ", best_key)
            print("解密结果: ", shift_cipher_decrypt(ciphertext, best_key))
            print("--------------------------------")
        else:
            break


if __name__ == '__main__':
    main()

2.仿射密码

(1)设计思路

仿射密码也是单表代替密码,通过仿射变换来构造代替表。

密钥k=(a,b),其中gcd(a,26)=1

加密函数:E_k(m)=(am+b)\mod 26=c

解密函数:D_k(c)=a^{-1}(c-b)\mod 26=m

(2)实现代码

# 加密
def affine_cipher_encrypt(plaintext, a, b):
    ciphertext = ""
    for char in plaintext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行加密
            if (char.isupper()):
                ciphertext += chr((a*(ord(char) - 65) +b) % 26 + 65)
            else:
                ciphertext += chr((a*(ord(char) - 97) +b) % 26 + 97)
        else:
            ciphertext += char
    return ciphertext


# 解密
def affine_cipher_decrypt(ciphertext, a, b):
    plaintext = ""
    a_inv = mod_inverse(a,26)
    for char in ciphertext:
        # 判断输入的是否为字母
        if char.isalpha():
            # 按字母的大小写进行解密
            if (char.isupper()):
                plaintext += chr(a_inv*((ord(char) - 65)-b) % 26 + 65)
            else:
                plaintext += chr(a_inv*((ord(char) - 97)-b) % 26 + 97)
        else:
            plaintext += char
    return plaintext

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

def mod_inverse(a,m):
    for x in range(1, m):
        if (a * x) % m == 1:
            return x
    raise ValueError("没有模逆存在")

# 主函数
def main():
    while True:
        x = input("请选择模式(1.加密 2.解密 3.退出):")
        if x == "1":
            print("-------------加密过程-------------")
            plaintext = input("请输入明文: ")
            print("请输入密钥:")
            a = int(input("a= "))
            b = int(input("b= "))
            if gcd(a, 26) != 1:
                raise ValueError("a and 26 must be coprime.")
            print("加密结果: ", affine_cipher_encrypt(plaintext, a, b))
            print("--------------------------------")
        elif x == "2":
            print("-------------解密过程-------------")
            ciphertext = input("请输入密文: ")
            print("请输入密钥:")
            a = int(input("a= "))
            b = int(input("b= "))
            if gcd(a, 26) != 1:
                raise ValueError("a and 26 must be coprime.")
            print("解密结果: ", affine_cipher_decrypt(ciphertext, a, b))
            print("--------------------------------")
        else:
            break


if __name__ == '__main__':
    main()

(3)实验结果

使用仿射密码加密:

使用仿射密码解密:

3.维吉尼亚密码

(1)设计思路

维吉尼亚密码是一种多表代替密码,同一明文字符可以被加密成不同的密文字符。使用一个固定长度的词组作密钥,通过重复密钥来决定每个字母的移位。

加密函数:E_k(m)=(m_1+k_1,\ldots ,m_d+k_d,m_{d+1}+k_1, m_{d+2}+k_2,\ldots)\mod 26

解密函数:D_k(c)=(c_1-k_1,\ldots,c_d-k_d, c_{d+1}-k_1, c_{d+2}-k_2,\ldots) \mod 26

设计思路:把明密文和密钥放入列表中用下标索引读取再加解密

(2)实现代码

# 加密
def vigenere_cipher_encrypt(plaintext, key):
    plaintext = ''.join(char for char in plaintext if 'a' <= char <= 'z')
    ciphertext = ""
    key_length = len(key)
    key_int = [ord(i) for i in key]
    plaintext_int = [ord(i) for i in plaintext]
    for i in range(len(plaintext_int)):
        value = (plaintext_int[i] - 97 + key_int[i % key_length] - 97) % 26
        ciphertext += chr(value + 97)
    return ciphertext


# 解密
def vigenere_cipher_decrypt(ciphertext, key):
    plaintext = ""
    key_length = len(key)
    key_int = [ord(i) for i in key]
    ciphertext_int = [ord(i) for i in ciphertext]
    for i in range(len(ciphertext_int)):
        value = (ciphertext_int[i] - key_int[i % key_length]) % 26
        plaintext += chr(value + 97)
    return plaintext


# 主函数
def main():
    while True:
        x = input("请选择模式(1.加密 2.解密 3.退出):")
        if x == "1":
            print("-------------加密过程-------------")
            plaintext = input("请输入明文: ")
            key = input("请输入密钥: ")
            print("加密结果: ", vigenere_cipher_encrypt(plaintext, key))
            print("--------------------------------")
        elif x == "2":
            print("-------------解密过程-------------")
            ciphertext = input("请输入密文: ")
            key = input("请输入密钥: ")
            print("解密结果: ", vigenere_cipher_decrypt(ciphertext, key))
            print("--------------------------------")
        else:
            break


if __name__ == '__main__':
    main()

注:在解密过程中应该将密文字符和密钥字符的ASCLL码值各减去97再相减,由于在相减的过程中97被消去,故代码中直接将密文字符和密钥字符的ASCLL值相减

(3)实验结果

加密结果:

解密结果:

4.维吉尼亚密码分析

破译维吉尼亚密码的关键在于确定密钥的长度。一旦知道了密钥长度,密文就可以被分成多列,每列对应一个移位密码,从而可以单独进行破解。这里介绍一个常用的方法来确定密钥长度:重合指数法

(1)重合指数的定义

重合指数(Index of Coincidence, IC)是用于评估文本中两个随机选取的字母相同的概率的统计量,在密码分析中帮助确定密钥长度。设某种语言由 m 个字母组成,每个字母 i出现的概率为 p_i。重合指数是指两个随机选取的字母相同的概率,记为 IC,其计算公式为:IC=\sum_{i=0}^{m-1}p_i^2

(2)原理

使用重和指数区分随机文本和自然语言文本其原理是:随机文本和自然语言文本中字母出现的概率不同。

随机文本的IC值:对于完全随机的文本,由于每个字母出现的概率相等,重合指数接近于0.038。如果一个文本是由26个字母随机组成的,每个字母出现的概率为 \frac{1}{26},两个字母相同的概率为 (\frac{1}{26})^2,由于共有26个字母,故:IC=26\times (\frac{1}{26})^2\approx 0.038

自然语言文本的IC值:对于自然语言(如英文)文本,由于某些字母出现的频率较高(例如,字母E在英文中出现的频率最高),重合指数接近于0.065。英文文本中的字母出现频率不同,假设各字母出现的概率分别为 p_0, p_1, \ldots, p_{25}那么两个字母相同的概率为:IC=\sum_{0}^{25}p_i^2\approx 0.065

分组密文的IC值:在使用维吉尼亚密码加密时,如果我们猜测的密钥长度m是正确的,则密文可以被分成m个组,每一组都类似于自然语言文本的移位密码。因此,每一组的IC值应接近自然语言文本的IC值,即0.065。

(3)具体步骤

  1. 猜测密钥长度 m:从1开始逐渐增加,依次猜测不同的密钥长度。

  2. 将密文分为 m 组:将密文按密钥长度 m 分组。每一组相当于一个移位密码。

  3. 计算每组的IC值:

    • 对于每一组,计算每个字母在该组中出现的次数 f_i
    • 计算该组的重合指数(IC值)。IC = \frac{\sum_{i=0}^{25} f_i (f_i - 1)}{n(n-1)}
  4. 计算 m 组的IC均值:

    • 计算所有分组的IC均值。IC_{\text{mean}} = \frac{1}{m} \sum_{j=1}^{m} IC_j
    • 若均值接近0.065,则 m 是正确的密钥长度。
    • 若均值接近0.038,则 m 不是正确的密钥长度。

(4)实现代码

代码思路:

        1.从1开始猜测密钥长度(设置一个最长的值max_key_length)

        2.使用重合指数法计算每一种密钥长度所对应的IC均值

        3.比较IC均值,取IC均值最接近0.065对应的密钥长度

        4.根据猜测的密钥长度将密文分组,每一组对应一个移位密码,根据移位密码的频率分析法确定密钥

        5.根据密钥对每一组移位密码解密

具体代码:

from collections import Counter


# 计算重合指数
def calculate_ic(text):
    n = len(text)
    freqs = Counter(text)
    ic = sum(f * (f - 1) for f in freqs.values()) / (n * (n - 1))
    return ic


# 通过重合指数法找到最可能的密钥长度
def find_key_length_ic(ciphertext, max_key_length=20):
    ciphertext = ''.join(char for char in ciphertext if 'a' <= char <= 'z')

    best_key_length = 1
    best_ic_mean = float('inf')

    for m in range(1, max_key_length + 1):
        ic_values = []
        for i in range(m):
            group = ''.join(ciphertext[j] for j in range(i, len(ciphertext), m))
            if len(group) > 1:
                ic_values.append(calculate_ic(group))

        if ic_values:
            ic_mean = sum(ic_values) / len(ic_values)
            print(f"密钥长度 {m} 的 IC 均值为 {ic_mean:.4f}")

            if abs(ic_mean - 0.065) < abs(best_ic_mean - 0.065):
                best_ic_mean = ic_mean
                best_key_length = m

    return best_key_length


# 解密函数
def vigenere_decrypt(ciphertext, key):
    key_length = len(key)
    key_as_int = [ord(i) for i in key]
    ciphertext_int = [ord(i) for i in ciphertext]
    plaintext = ''
    for i in range(len(ciphertext_int)):
        value = (ciphertext_int[i] - key_as_int[i % key_length]) % 26
        plaintext += chr(value + 65)
    return plaintext.lower()


# 找到密钥的每一个字母
def find_vigenere_key(ciphertext, key_length):
    ciphertext = ''.join(char for char in ciphertext if 'a' <= char <= 'z')
    key = ''
    for i in range(key_length):
        segment = ''.join(ciphertext[j] for j in range(i, len(ciphertext), key_length))
        key += find_caesar_shift(segment)
    return key


# 通过频率分析法找到凯撒密码的偏移量
def find_caesar_shift(segment):
    english_frequencies = {
        'a': 0.082, 'b': 0.015, 'c': 0.028, 'd': 0.043, 'e': 0.127, 'f': 0.022,
        'g': 0.020, 'h': 0.061, 'i': 0.070, 'j': 0.002, 'k': 0.008, 'l': 0.040,
        'm': 0.024, 'n': 0.067, 'o': 0.075, 'p': 0.019, 'q': 0.001, 'r': 0.060,
        's': 0.063, 't': 0.091, 'u': 0.028, 'v': 0.010, 'w': 0.023, 'x': 0.001,
        'y': 0.020, 'z': 0.001
    }

    segment_frequencies = Counter(segment)
    n = len(segment)

    chi_squared_statistics = []
    for shift in range(26):
        chi_squared = 0
        for letter in english_frequencies:
            observed = segment_frequencies[chr((ord(letter) - 97 + shift) % 26 + 97)]
            expected = english_frequencies[letter] * n
            chi_squared += (observed - expected) ** 2 / expected
        chi_squared_statistics.append(chi_squared)

    return chr(chi_squared_statistics.index(min(chi_squared_statistics)) + 97)

def main():
    # 示例密文
    #ciphertext = "vvhqwvvrhmusgjgthkihtssejchlsfcbgvwcrlryqtfsvgahwkcuhwauglqhnslrljshbltspisprdxljsveegh lqwkasskuwepwqtwvspgoelkcqyfnsvwljsniqkgnrgybwlwgoviokhkazkqkxzgyhcecmeiujoqkwfwvefqhkijrclrlkbienqfrjljsdhgrhlsfqtwlauqrhwdmwlgusgikkflryvcwvspgpmlkassjvoqxeggveyg gzmljcxxljsvpaivwikvrdrygfrjljslveggveyggeiapuuisfpbtgnwwmuczrvtwglrwugumnczvile"

    ciphertext = input("请输入密文:")

    key_length = find_key_length_ic(ciphertext)
    print(f"推测的密钥长度为: {key_length}")

    key = find_vigenere_key(ciphertext, key_length)
    print(f"推测的密钥为: {key}")

    plaintext = vigenere_decrypt(ciphertext, key)
    print(f"解密后的明文为: {plaintext}")

if __name__ == '__main__':
    main()

 实验结果:

二、分组密码

分组密码也叫块密码(Block Cipher),明、密文分组(64bit,128bit等)进行加解密,加解密使用相同的密钥。

1.DES加密算法

(1)设计思路

算法原理:DES算法原理及实现 (ruanx.net)(这篇介绍很详细)

加密流程:

设计目标:给定明文0x0123456789abcdef,密钥0x1f1f1f1f0e0e0e0e,得到相应密文

设计思路:

1.明密文、密钥的输入输出处理(输入和输出的都是是十六进制数,但在加密过程中使用的是元素为0\1的列表)

 (1)十六进制数与n位二进制列表的转换:

def hex2bit(hex_val,n):
    bit_list = [int(i) for i in bin(hex_val)[2:].zfill(n)]# 确保hex_val是一个int整型
    return bit_list
assert hex2bit(0x1a,10) == [0, 0, 0, 0, 0, 1, 1, 0, 1, 0]# 测试案例
def bit2hex(bit_list):
    bit_str = ''.join(str(bit) for bit in bit_list)# 将二进制列表里的元素转化为二进制字符串
    hex_val = hex(int(bit_str ,2))# 将二进制字符串转换为十六进制字符串
    return hex_val
assert bit2hex([0, 0, 0, 0, 0, 1, 1, 0, 1, 0]) == '0x1a'

2.基础运算

(1)循环左移n位

def leftRotate(bit_list, n):
    return bit_list[n:] + bit_list[:n]

assert leftRotate([1, 1, 0, 0], 3) == [0, 1, 1, 0]

(2)异或运算

def bitXor(bit_list_1, bit_list_2):
    return [a^b for a, b in zip(bit_list_1, bit_list_2)]

assert bitXor([1, 1, 1, 0], [0, 1, 0, 1]) == [1, 0, 1, 1]

(3)置换操作

给定一个置换列表,将二进制列表中的元素进行换位,以IP置换为例

def IP(a):
    ip = [58, 50, 42, 34, 26, 18, 10, 2,
          60, 52, 44, 36, 28, 20, 12, 4,
          62, 54, 46, 38, 30, 22, 14, 6,
          64, 56, 48, 40, 32, 24, 16, 8,
          57, 49, 41, 33, 25, 17, 9, 1,
          59, 51, 43, 35, 27, 19, 11, 3,
          61, 53, 45, 37, 29, 21, 13, 5,
          63, 55, 47, 39, 31, 23, 15, 7]
    return [a[x - 1] for x in ip]# 返回一个列表,列表中的第一个元素是a的第58个元素(即a[57]),第2个元素是a的第50个元素(即a[49])

3.S盒的输入输出(48bit→32bit)

(1)DES有8个S盒,输入的48bit分成8组6bit,每组进入一个S盒。每个S盒都是一个4行16列的表,表中每个元素为一个4位二进制(以十进制数表示,即0~15),具体的S盒替代过程如下:

        输入6bit,取最高位和最低位作为行数,中间4位作为列数

        在S盒中找对应行数和列数的值作为输出

(2)对于一个S盒(长度为4*16的列表),怎么找到特定行列的元素

思路:例如一个2*4的列表,两行可以用1比特表示(第0行:0;第1行:1),四列用2比特表示(第0列:00;第1列:01;第2列:10;第3列:11),那么这个列表中每个元素的索引位置的二进制表示如下:

[000 001 010 011

 100 101 110 111]

说明行号的比特位在列号的比特位前面

举例:现在想找到一个2*4列表(记为a)的第1行第3列的元素,行号1=0b1,列号3=0b11,拼起来就是0b111(行在前列在后),对应十进制的7,那么这个元素就是a[7]

(3)具体代码

# 导入numpy包用于数组处理

import numpy as np 

a = np.array(a).reshape(8, 6)# 把输入的48bit分成8组,每组单独处理,注意重塑之前要先用array转为可重塑的数组

(2)实现代码

import numpy as np

# 把输入的十六进制数转化为二进制列表表示
def hex2bin(hex_val,n):
    bin_list = [int(i) for i in bin(hex_val)[2:].zfill(n)]
    return bin_list

# 把输入的二进制列表转化成十六进制字符串
def bin2hex(bit_list):
    bin_str = ''.join(str(bit) for bit in bit_list)# 将二进制列表里的元素转化为二进制字符串
    hex_val = hex(int(bin_str ,2))# 将二进制字符串转换为十六进制字符串
    return hex_val

# 循环左移运算
def leftRotate(bit_list, n):
    return bit_list[n:] + bit_list[:n]

assert leftRotate([1, 1, 0, 0], 3) == [0, 1, 1, 0]

# 异或运算
def bitXor(bit_list_1, bit_list_2):
    return [a^b for a, b in zip(bit_list_1, bit_list_2)]

assert bitXor([1, 1, 1, 0], [0, 1, 0, 1]) == [1, 0, 1, 1]

# IP置换(IP置换后分成左右两部分)
def IP(a):
    ip1 = [58, 50, 42, 34, 26, 18, 10, 2,
            60, 52, 44, 36, 28, 20, 12, 4,
            62, 54, 46, 38, 30, 22, 14, 6,
            64, 56, 48, 40, 32, 24, 16, 8]
    ip2 = [ 57, 49, 41, 33, 25, 17, 9, 1,
            59, 51, 43, 35, 27, 19, 11, 3,
            61, 53, 45, 37, 29, 21, 13, 5,
            63, 55, 47, 39, 31, 23, 15, 7]
    return [a[x-1] for x in ip1], [a[x-1] for x in ip2]

# IP置换的逆,也是最终置换
def FP(a):
    fp = [40, 8, 48, 16, 56, 24, 64, 32,
            39, 7, 47, 15, 55, 23, 63, 31,
            38, 6, 46, 14, 54, 22, 62, 30,
            37, 5, 45, 13, 53, 21, 61, 29,
            36, 4, 44, 12, 52, 20, 60, 28,
            35, 3, 43, 11, 51, 19, 59, 27,
            34, 2, 42, 10, 50, 18, 58, 26,
            33, 1, 41, 9, 49, 17, 57, 25]
    return [a[x-1] for x in fp]

# 选择置换1
def PC1(key):
    pc1_l = [57, 49, 41, 33, 25, 17, 9,
             1, 58, 50, 42, 34, 26, 18,
             10, 2, 59, 51, 43, 35, 27,
             19, 11, 3, 60, 52, 44, 36]
    pc1_r = [63, 55, 47, 39, 31, 23, 15,
             7, 62, 54, 46, 38, 30, 22,
             14, 6, 61, 53, 45, 37, 29,
             21, 13, 5, 28, 20, 12, 4]

    return [key[x - 1] for x in pc1_l], [key[x - 1] for x in pc1_r]

# 选择置换2
def PC2(key):
    pc2 = [14, 17, 11, 24, 1, 5,
            3, 28, 15, 6, 21, 10,
            23, 19, 12, 4, 26, 8,
            16, 7, 27, 20, 13, 2,
            41, 52, 31, 37, 47, 55,
            30, 40, 51, 45, 33, 48,
            44, 49, 39, 56, 34, 53,
            46, 42, 50, 36, 29, 32]

    return [key[x-1] for x in pc2]

# 密钥扩展算法(从64bit的密钥生成16个48bit的子密钥)
def keyGen(key):
    assert len(key) == 64

    l,r = PC1(key)
    off = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]# 循环左移的位数
    res = []
    for i in range(16):
        l = leftRotate(l, off[i])
        r = leftRotate(r, off[i])
        res.append(PC2(l + r))

    return res

# S盒替换
def S(a):
    assert len(a) == 48

    S_box = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
              0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
              4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
              15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
             [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
              3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
              0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
              13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
             [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
              13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
              13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
              1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
             [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
              13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
              10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
              3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
             [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
              14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
              4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
              11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
             [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
              10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
              9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
              4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
             [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
              13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
              1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
              6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
             [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
              1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
              7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
              2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]

    a = np.array(a).reshape(8,6)# 注意,数组重塑行列之前要把列表转化为数组即先使用array

    res = []
    for i in range(8):
        # 把二进制列表转化为十进制数
        str_val = ''.join(str(num) for num in [a[i][0],a[i][5],a[i][1],a[i][2],a[i][3],a[i][4]])
        j = int(str_val, base = 2)
        s = S_box[i][j]
        res += [int(x) for x in bin(s)[2:].zfill(4)]

    return res

# E扩展(32比特→48比特)
def Expand(a):
    assert len(a) == 32
    e = [32, 1, 2, 3, 4, 5,
        4, 5, 6, 7, 8, 9,
        8, 9, 10, 11, 12, 13,
        12, 13, 14, 15, 16, 17,
        16, 17, 18, 19, 20, 21,
        20, 21, 22, 23, 24, 25,
        24, 25, 26, 27, 28, 29,
        28, 29, 30, 31, 32, 1]
    return [a[x-1] for x in e]

# P置换
def P(a):
    assert len(a) == 32

    p = [16, 7, 20, 21,
        29, 12, 28, 17,
        1, 15, 23, 26,
        5, 18, 31, 10,
        2, 8, 24, 14,
        32, 27, 3, 9,
        19, 13, 30, 6,
        22, 11, 4, 25]
    return [a[x-1] for x in p]

# F函数
def F(r, k):
    assert len(r) == 32
    assert len(k) == 48

    r = Expand(r)
    s = S(bitXor(r, k))
    p = P(s)
    return p

# 轮函数
def goRound(l,r,sub_key):
    return r, bitXor(l, F(r, sub_key))

def des(plaintext, key, method):
    subkeys = keyGen(hex2bin(key, 64))

    if method == 'decrypt':
        subkeys = subkeys[::-1]# 加解密的算法一致,只是密钥使用顺序相反

    l, r = IP(hex2bin(plaintext, 64))
    for i in range(16):
        l ,r = goRound(l, r, subkeys[i])

    return bin2hex(FP(r + l))


def main():
    while True:
        x = input("请选择模式:1.DES加密 2.DES解密 3.退出")
        if x == '1':
            print("-------------加密过程-------------")
            plaintext = int(input("请输入明文:"), 16)
            key = int(input("请输入密钥:"), 16)
            ciphertext = des(plaintext,key,'encrypt')
            print("加密结果:",ciphertext)
            print("--------------------------------")
        elif x == '2':
            print("-------------解密过程-------------")
            ciphertext = int(input("请输入密文:"), 16)
            key = int(input("请输入密钥:"), 16)
            plaintext = des(ciphertext, key, 'decrypt')
            print("解密结果:", plaintext)
            print("--------------------------------")
        else:
            break


if __name__ == '__main__':
    main()

(3)实验结果

(a)测试:

        加密:明文:0x11aabbccddeeff01;密钥:0xcafababedeadbeaf ;加密后的结果:0x2973a7e54ec730a3

        解密:密文:0x2973a7e54ec730a3;密钥:0xcafababedeadbeaf;解密后的结果:0x11aabbccddeeff01

(b) 加密结果:

2.AES加密算法

(1)设计思路

state:是一个4×4的矩阵,由16个字节组成,例如:

state = [

             [0x00,0x04,0x08,0x0c],

             [0x01,0x05,0x09,0x0d],

             [0x02,0x06,0x0a,0x0e],

             [0x03,0x07,0x0b,0x0f]

             ]

(2)实现代码

S_BOX = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
         0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
         0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
         0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
         0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
         0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
         0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
         0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
         0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
         0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
         0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
         0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
         0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
         0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
         0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
         0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
         ]

R_CON = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]

# 字节代换
def sub_bytes(state):
    for i in range(4):
        for j in range(4):
            state[i][j] = S_BOX[state[i][j]]
    return state

assert sub_bytes([[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a]]) == \
    [[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2]]

# 行移位
def shift_rows(state):
    for i in range(4):
        state[i] = state[i][i:] + state[i][:i]
    return state

assert shift_rows([[0x00,0x04,0x08,0x0c],[0x01,0x05,0x09,0x0d],[0x02,0x06,0x0a,0x0e],[0x03,0x07,0x0b,0x0f]]) == \
    [[0x00,0x04,0x08,0x0c],[0x05,0x09,0x0d,0x01],[0x0a,0x0e,0x02,0x06],[0x0f,0x03,0x07,0x0b]]

def gmul(a, b):
    """GF(2^8)有限域上的乘法"""
    p = 0
    for _ in range(8):
        if b & 1:
            p ^= a
        hi_bit_set = a & 0x80   # 检查a的第7位是否为1
        a <<= 1
        if hi_bit_set:
            a ^= 0x1b  # 0x11b是AES中使用的不可约多项式
        b >>= 1
    return p & 0xFF

assert gmul(0xc3,0x23) == 0x4d

def mix_single_column(a):
    """对一列进行列混合"""
    t = a[0] ^ a[1] ^ a[2] ^ a[3]
    b0 = a[0] ^ t ^ gmul(a[0] ^ a[1], 0x02)
    b1 = a[1] ^ t ^ gmul(a[1] ^ a[2], 0x02)
    b2 = a[2] ^ t ^ gmul(a[2] ^ a[3], 0x02)
    b3 = a[3] ^ t ^ gmul(a[3] ^ a[0], 0x02)
    return [b0, b1, b2, b3]

assert mix_single_column([0x87, 0x6e, 0x46, 0xa6]) == [0x47, 0x37, 0x94, 0xed]

# 列混合
def mix_columns(state):
    for i in range(4):
        col = [state[j][i] for j in range(4)]
        col = mix_single_column(col)
        for j in range(4):
            state[j][i] = col[j]
    return state

def sub_word(word):
    """对一个4字节的word执行S-box替换"""
    return [S_BOX[b] for b in word]

def rot_word(word):
    """对一个4字节的word执行循环左移"""
    return word[1:] + word[:1]

# 密钥扩展
def key_expansion(key):
    assert len(key) == 16  # AES-128 使用16字节的密钥

    # 生成44个32位的字
    round_keys = []
    for i in range(4):
        round_keys.append([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]])

    for i in range(4, 4 * (10 + 1)):
        temp = round_keys[i - 1]
        if i % 4 == 0:
            temp = sub_word(rot_word(temp))
            rcon = (R_CON[i // 4 - 1] >> 24)  # 提取 R_CON 的高字节
            temp[0] ^= rcon
        round_keys.append([round_keys[i - 4][j] ^ temp[j] for j in range(4)])

    # 将所有的轮密钥连接成一个大的数组
    expanded_key = []
    for word in round_keys:
        expanded_key.extend(word)

    return [expanded_key[i:i + 16] for i in range(0, len(expanded_key), 16)]

# 轮密钥加
def add_round_key(state, round_key):
    for i in range(4):
        for j in range(4):
            state[j][i] ^= round_key[i * 4 + j]
    return state

# AES加密算法
def aes_encrypt(plaintext, key):
    state = [[0] * 4 for _ in range(4)]
    for i in range(16):
        state[i % 4][i // 4] = plaintext[i]

    round_keys = key_expansion(key)

    state = add_round_key(state, round_keys[0])

    for round in range(1, 10):
        state = sub_bytes(state)
        state = shift_rows(state)
        state = mix_columns(state)
        state = add_round_key(state, round_keys[round])

    state = sub_bytes(state)
    state = shift_rows(state)
    state = add_round_key(state, round_keys[10])

    ciphertext = [0] * 16
    for i in range(16):
        ciphertext[i] = state[i % 4][i // 4]

    return bytes(ciphertext)

def main():
    plaintext = b'\x32\x43\xf6\xa8\x88\x5a\x30\x8d\x31\x31\x98\xa2\xe0\x37\x07\x34'
    key = b'\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\xcf\x1f\x19\x2b\x21\x37'
    ciphertext = aes_encrypt(plaintext, key)
    print("加密结果是:",ciphertext.hex())


if __name__ == '__main__':
    main()

(3)实验结果

3.SM4加密算法

(1)设计思路

SM4分组密码算法国标:

在线预览|GB/T 32907-2016 (gb688.cn)

(2)实现代码

S_BOX = [0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
         0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
         0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
         0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
         0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
         0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
         0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
         0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
         0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
         0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
         0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
         0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
         0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
         0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
         0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
         0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
         ]

FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
    ]

# 循环移位
def rot(X, n):
    return (X << n) & 0xffffffff | (X >> (32 - n))

# 轮函数F
def F(X, rk):
    A = X[1] ^ X[2] ^ X[3] ^ rk
    B = S_BOX[(A >> 24) & 0xff] << 24 | S_BOX[(A >> 16) & 0xff] << 16 | S_BOX[(A >> 8) & 0xff] << 8 | S_BOX[A & 0xff]
    C = B ^ rot(B, 2) ^ rot(B, 10) ^ rot(B, 18) ^ rot(B, 24)
    return X[0] ^ C

# 密钥扩展算法里的轮函数
def F_(X, i):
    A = X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ CK[i]
    B = S_BOX[(A >> 24) & 0xff] << 24 | S_BOX[(A >> 16) & 0xff] << 16 | S_BOX[(A >> 8) & 0xff] << 8 | S_BOX[A & 0xff]
    C = B ^ rot(B, 13) ^ rot(B, 23)
    return X[i] ^ C

# 密钥扩展算法
def generateKey(MK):
    K = []
    for i in range(4):
        K.append(((MK >> (32 * (3 - i))) & 0xffffffff) ^ FK[i])

    for i in range(32):
        K.append(F_(K, i))

    return K[4:]

# SM4加密算法
def SM4Encryption(plaintext, key):
    rk = generateKey(key)
    X = []
    for i in range(4):
        X.append((plaintext >> (32 * (3 - i))) & 0xffffffff)

    for i in range(32):
        X.append(F(X[i:i + 4], rk[i]))

    Y = ''.join([hex(X[i])[2:].zfill(8) for i in range(-1, -5, -1)])
    return hex(int(Y,base = 16))

# SM4解密算法
def SM4Decryption(ciphertext, key):
    rk = generateKey(key)
    X = []
    for i in range(4):
        X.append((ciphertext >> (32 * (3 - i))) & 0xffffffff)

    for i in range(32):
        X.append(F(X[i:i + 4], rk[31 - i]))# 加解密算法一致,只是密钥使用顺序相反

    Y = ''.join([hex(X[i])[2:].zfill(8) for i in range(-1, -5, -1)])
    return hex(int(Y,base = 16))

if __name__ == "__main__":
    while True:
        x = int(input("请选择模式:1.SM4加密 2.SM4解密 3.退出"))
        if x == 1:
            print("-------------SM4加密过程-------------")
            plaintext = int(input("请输入明文:"), base=16)
            key = int(input("请输入密钥:"), base=16)
            ciphertext = SM4Encryption(plaintext, key)
            print("加密结果为:", ciphertext)
            print("-----------------------------------")
        elif x == 2:
            print("-------------SM4解密密过程-------------")
            ciphertext = int(input("请输入密文:"), base=16)
            key = int(input("请输入密钥:"), base=16)
            plaintext = SM4Decryption(ciphertext, key)
            print("解密结果为:", plaintext)
            print("-----------------------------------")
        else:
            break

(3)实验结果

选择明文:0x0123456789ABCDEFFEDCBA9876543210 及密钥:0x0123456789ABCDEFFEDCBA9876543210 输出的密文如下,与上面的示例相同

三、序列密码(ZUC算法)

序列密码也称流密码(Stream Cipher),明、密文序列的1位(1 bit或1 byte)进行加解密,加解密使用相同的密钥。

1.算法原理

参考:

祖冲之加密算法详解及代码实现_祖冲之算法-CSDN博客

在线预览|GB/T 33133.1-2016 (gb688.cn)(国标里ZUC算法的描述,来自国家标准全文公开 (samr.gov.cn),其中ZUC算法的标准号是33133)

【以ZUC算法为例介绍学习方法】| 祖冲之序列密码算法| 序列密码| 加密算法| 密码学| |国密| 学习方法_哔哩哔哩_bilibili

(【可厉害土豆】的视频讲解,结构讲得超清晰,下面很多结构图也是截取她视频里的)

算法结构分成上中下三层:

(1)LFSR

初始化模式:输入一个31比特字u,对16个31比特寄存器单元变量s_0,s_1,\dots,s_{15}进行更新(更新前的初始状态是由密钥k和初始向量iv决定的)

工作模式:无输入,直接对寄存器单元变量s_0,s_1,\dots,s_{15}进行更新

(2)比特重组BR

输入LFSR寄存器单元变量s_0,s_1,\dots,s_{15},输出4个32比特字X_0,X_1,X_2,X_3(即从寄存器单元变量s_0,s_1,\dots,s_{15}从抽取128比特组成4个32比特的字)

(3)非线性函数F

输入(来自上层BR的)3个32比特字X_0,X_1,X_2,X_3,输出一个32比特字W(非线性函数F相当于一个把96比特压缩成32比特的压缩函数),非线性函数F包含一个S盒和2个32比特的存储单元R_1,R_2

2.算法执行

ZUC算法的执行分成两个阶段

(1)初始化阶段

密钥和初始向量初始化,算法运行但不产生任何输出。目的是得到16个寄存器单元变量s_0,s_1,\dots,s_{15}

(2)工作阶段

每次运行产生1个字的输出并且更新寄存器单元变量s_0,s_1,\dots,s_{15}在下一轮的BR中使用,共运行L次(L由明文长度决定L=\left \lceil \frac{length}{32} \right \rceil,即明文长度除32再向上取整)

3.算法实现

import math

S0 = [
    0x3E, 0x72, 0x5B, 0x47, 0xCA, 0xE0, 0x00, 0x33, 0x04, 0xD1, 0x54, 0x98, 0x09, 0xB9, 0x6D, 0xCB,
    0x7B, 0x1B, 0xF9, 0x32, 0xAF, 0x9D, 0x6A, 0xA5, 0xB8, 0x2D, 0xFC, 0x1D, 0x08, 0x53, 0x03, 0x90,
    0x4D, 0x4E, 0x84, 0x99, 0xE4, 0xCE, 0xD9, 0x91, 0xDD, 0xB6, 0x85, 0x48, 0x8B, 0x29, 0x6E, 0xAC,
    0xCD, 0xC1, 0xF8, 0x1E, 0x73, 0x43, 0x69, 0xC6, 0xB5, 0xBD, 0xFD, 0x39, 0x63, 0x20, 0xD4, 0x38,
    0x76, 0x7D, 0xB2, 0xA7, 0xCF, 0xED, 0x57, 0xC5, 0xF3, 0x2C, 0xBB, 0x14, 0x21, 0x06, 0x55, 0x9B,
    0xE3, 0xEF, 0x5E, 0x31, 0x4F, 0x7F, 0x5A, 0xA4, 0x0D, 0x82, 0x51, 0x49, 0x5F, 0xBA, 0x58, 0x1C,
    0x4A, 0x16, 0xD5, 0x17, 0xA8, 0x92, 0x24, 0x1F, 0x8C, 0xFF, 0xD8, 0xAE, 0x2E, 0x01, 0xD3, 0xAD,
    0x3B, 0x4B, 0xDA, 0x46, 0xEB, 0xC9, 0xDE, 0x9A, 0x8F, 0x87, 0xD7, 0x3A, 0x80, 0x6F, 0x2F, 0xC8,
    0xB1, 0xB4, 0x37, 0xF7, 0x0A, 0x22, 0x13, 0x28, 0x7C, 0xCC, 0x3C, 0x89, 0xC7, 0xC3, 0x96, 0x56,
    0x07, 0xBF, 0x7E, 0xF0, 0x0B, 0x2B, 0x97, 0x52, 0x35, 0x41, 0x79, 0x61, 0xA6, 0x4C, 0x10, 0xFE,
    0xBC, 0x26, 0x95, 0x88, 0x8A, 0xB0, 0xA3, 0xFB, 0xC0, 0x18, 0x94, 0xF2, 0xE1, 0xE5, 0xE9, 0x5D,
    0xD0, 0xDC, 0x11, 0x66, 0x64, 0x5C, 0xEC, 0x59, 0x42, 0x75, 0x12, 0xF5, 0x74, 0x9C, 0xAA, 0x23,
    0x0E, 0x86, 0xAB, 0xBE, 0x2A, 0x02, 0xE7, 0x67, 0xE6, 0x44, 0xA2, 0x6C, 0xC2, 0x93, 0x9F, 0xF1,
    0xF6, 0xFA, 0x36, 0xD2, 0x50, 0x68, 0x9E, 0x62, 0x71, 0x15, 0x3D, 0xD6, 0x40, 0xC4, 0xE2, 0x0F,
    0x8E, 0x83, 0x77, 0x6B, 0x25, 0x05, 0x3F, 0x0C, 0x30, 0xEA, 0x70, 0xB7, 0xA1, 0xE8, 0xA9, 0x65,
    0x8D, 0x27, 0x1A, 0xDB, 0x81, 0xB3, 0xA0, 0xF4, 0x45, 0x7A, 0x19, 0xDF, 0xEE, 0x78, 0x34, 0x60
]

S1 = [
    0x55, 0xC2, 0x63, 0x71, 0x3B, 0xC8, 0x47, 0x86, 0x9F, 0x3C, 0xDA, 0x5B, 0x29, 0xAA, 0xFD, 0x77,
    0x8C, 0xC5, 0x94, 0x0C, 0xA6, 0x1A, 0x13, 0x00, 0xE3, 0xA8, 0x16, 0x72, 0x40, 0xF9, 0xF8, 0x42,
    0x44, 0x26, 0x68, 0x96, 0x81, 0xD9, 0x45, 0x3E, 0x10, 0x76, 0xC6, 0xA7, 0x8B, 0x39, 0x43, 0xE1,
    0x3A, 0xB5, 0x56, 0x2A, 0xC0, 0x6D, 0xB3, 0x05, 0x22, 0x66, 0xBF, 0xDC, 0x0B, 0xFA, 0x62, 0x48,
    0xDD, 0x20, 0x11, 0x06, 0x36, 0xC9, 0xC1, 0xCF, 0xF6, 0x27, 0x52, 0xBB, 0x69, 0xF5, 0xD4, 0x87,
    0x7F, 0x84, 0x4C, 0xD2, 0x9C, 0x57, 0xA4, 0xBC, 0x4F, 0x9A, 0xDF, 0xFE, 0xD6, 0x8D, 0x7A, 0xEB,
    0x2B, 0x53, 0xD8, 0x5C, 0xA1, 0x14, 0x17, 0xFB, 0x23, 0xD5, 0x7D, 0x30, 0x67, 0x73, 0x08, 0x09,
    0xEE, 0xB7, 0x70, 0x3F, 0x61, 0xB2, 0x19, 0x8E, 0x4E, 0xE5, 0x4B, 0x93, 0x8F, 0x5D, 0xDB, 0xA9,
    0xAD, 0xF1, 0xAE, 0x2E, 0xCB, 0x0D, 0xFC, 0xF4, 0x2D, 0x46, 0x6E, 0x1D, 0x97, 0xE8, 0xD1, 0xE9,
    0x4D, 0x37, 0xA5, 0x75, 0x5E, 0x83, 0x9E, 0xAB, 0x82, 0x9D, 0xB9, 0x1C, 0xE0, 0xCD, 0x49, 0x89,
    0x01, 0xB6, 0xBD, 0x58, 0x24, 0xA2, 0x5F, 0x38, 0x78, 0x99, 0x15, 0x90, 0x50, 0xB8, 0x95, 0xE4,
    0xD0, 0x91, 0xC7, 0xCE, 0xED, 0x0F, 0xB4, 0x6F, 0xA0, 0xCC, 0xF0, 0x02, 0x4A, 0x79, 0xC3, 0xDE,
    0xA3, 0xEF, 0xEA, 0x51, 0xE6, 0x6B, 0x18, 0xEC, 0x1B, 0x2C, 0x80, 0xF7, 0x74, 0xE7, 0xFF, 0x21,
    0x5A, 0x6A, 0x54, 0x1E, 0x41, 0x31, 0x92, 0x35, 0xC4, 0x33, 0x07, 0x0A, 0xBA, 0x7E, 0x0E, 0x34,
    0x88, 0xB1, 0x98, 0x7C, 0xF3, 0x3D, 0x60, 0x6C, 0x7B, 0xCA, 0xD3, 0x1F, 0x32, 0x65, 0x04, 0x28,
    0x64, 0xBE, 0x85, 0x9B, 0x2F, 0x59, 0x8A, 0xD7, 0xB0, 0x25, 0xAC, 0xAF, 0x12, 0x03, 0xE2, 0xF2
]

D = [
    0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
    0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
]

# 初始
S = [0]*16
X = [0, 0, 0, 0]
R1 = 0
R2 = 0
Key_result = []
m = 2 ** 32

# 线性反馈移位寄存器LFSR的初始化模式
def LFSRWithInitMode(u):
    v = (2**15*S[15] + 2**17*S[13] + 2**21*S[10] + 2**20*S[4] + (1+2**8)*S[0]) % (2**31-1)
    S.append((v + u) % (2**31-1))

    if S[16] == 0:
        S[16] = 2**31 - 1

    S.pop(0)

# 线性反馈移位寄存器LFSR的工作模式
def LFSRWithWorkMode():
    S.append((2**15*S[15] + 2**17*S[13] + 2**21*S[10] + 2**20*S[4] + (1+2**8)*S[0]) % (2**31-1))

    if S[16] == 0:
        S[16] = 2**31 - 1

    S.pop(0)

def H(W):# 取出高16位
    return (W >> 15) & 0xffff

def L(W):# 取出低16位
    return W & 0xffff

def rot(W, n):# 循环左移n位
    return W << n & 0xffffffff | W >> (32-n)

# 比特重组BR
def BitReconstruction():
    X[0] = H(S[15]) << 16 | L(S[14])
    X[1] = L(S[11]) << 16 | H(S[9])
    X[2] = L(S[7]) << 16 | H(S[5])
    X[3] = L(S[2]) << 16 | H(S[0])


def L1(X):
    return X ^ rot(X, 2) ^ rot(X, 10) ^ rot(X, 18) ^ rot(X, 24)

def L2(X):
    return X ^ rot(X, 8) ^ rot(X, 14) ^ rot(X, 22) ^ rot(X, 30)

def S_Box(X):
    x0 = (X>>(32-4)&0xf)<<4 | X>>(32-8)&0xf
    x1 = (X>>(32-12)&0xf)<<4 | X>>(32-16)&0xf
    x2 = (X>>(32-20)&0xf)<<4 | X>>(32-24)&0xf
    x3 = (X>>(32-28)&0xf)<<4 | X&0xf
    Y = S0[x0]<<24 | S1[x1]<<16 | S0[x2]<<8 | S1[x3]
    return Y

# 非线性函数F
def F(X0, X1, X2):
    global R1, R2, W, S
    W = ((X0 ^ R1) + R2) % m
    W1 = (R1 + X1) % m
    W2 = R2 ^ X2
    R1 = S_Box(L1(((W1<<16) | (W2>>16)) & 0xffffffff))
    R2 = S_Box(L2(((W2<<16) | (W1>>16)) & 0xffffffff))

# 初始化步骤
def initialize(key, iv):
    global S
    S = [(key[i] << 23) | (D[i] << 8) | iv[i] for i in range(16)]
    for i in range(32):
        BitReconstruction()
        F(X[0], X[1], X[2])
        LFSRWithInitMode(W >> 1)

# 工作步骤
def work():
    BitReconstruction()
    F(X[0], X[1], X[2])
    LFSRWithWorkMode()
    for i in range(keylen):
        BitReconstruction()
        F(X[0], X[1], X[2])
        Key_result.append(W ^ X[3])
        LFSRWithWorkMode()


def print_key():  # 打印密钥
    for i in range(keylen):
        print('KEY' + str(i) + ':\033[1;36m' + hex(Key_result[i]).replace('0x', '') + '\033[0m ', end='')


if __name__ == '__main__':
    # 初始密钥和iv
    key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
    iv = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
    initialize(key, iv)
    while True:
        x = int(input("请选择模式:1.获得密钥字 2.ZUC加密 3.退出"))
        if x == 1:
            print("-------------获得密钥字-------------")
            keylen = int(input("请输入需要的密钥字个数:"))
            work()
            print_key()
            print("\n----------------------------------")
        elif x == 2:
            print("-------------ZUC加密--------------")
            plaintext = input("请输入需要加密的明文:")
            keylen = math.ceil(len(plaintext.replace("0x", ""))/8)
            work()
            key_result = int(''.join(str(i) for i in Key_result))
            ciphertext = hex(int(plaintext, base=16) ^ key_result)
            print("加密的结果是:",ciphertext)
            print("----------------------------------")
        else:
            break




加密结果: 

注:在函数内部对全局变量进行修改需要在函数内使用global关键字声明该变量 

四、公钥密码(RSA)

分组密码和序列密码都属于对称密码,即加解密使用相同的密钥。公钥密码属于非对称密码,加解密使用不同的密钥,其中公开的密钥称为公钥,私有的密钥称为私钥。

(1)RSA算法描述

RSA算法是一种广泛使用的公钥加密算法,基于大整数分解困难,安全性较高。

1. 密钥生成
选择两个大质数p,q:这两个质数应尽可能大且随机选取。
计算模数nn=p \times q。这个值将用于公钥和私钥中。
计算欧拉函数\varphi (n)=(p-1)(q-1)
选择公钥指数e: 选择一个整数 e,使得1 < e < \phi(n)e\varphi(n)互质。常见的选择是 65537。
计算私钥指数d:计算d使得d \times e \equiv 1 \ (\text{mod} \ \phi(n)),即de关于\varphi(n)的模逆元。

公钥:(e, n)
私钥:(d, n)

2. 加密
明文M:将待加密的信息转化为数字M,要求0 \leq M < n
密文C:使用公钥(e, n)对明文进行加密,计算C \equiv M^e \ (\text{mod} \ n)

3. 解密
解密明文M:使用私钥(d, n)对密文进行解密,计算M \equiv C^d \ (\text{mod} \ n)

(2)设计思路

1.确保选择的两个大数是质数【Miller-Rabin素性测试】

        Miller-Rabin 素性测试是一种基于概率的算法,用于判断一个数是否为素数。如果所有测试都没有指出n是合数,那么n是素数的概率非常高。

原理:【朝夕的ACM笔记】数论-Miller Rabin素数判定 - 知乎 (zhihu.com)

2.根据公钥计算私钥【扩展欧几里得算法】

        扩展欧几里得算法(Extended Euclidean Algorithm)是一种用于计算两个整数 a 和 b(假设 b≠0)的最大公因数(GCD, Greatest Common Divisor)的算法,并且还能找到整数 x 和 y,使得 ax+by=gcd(a,b)。算法核心:递归调用。

        公钥e所对应的私钥d满足ed\equiv 1 \ (\text{mod} \ \varphi(n)),转化为求ex+\varphi(n)y=1中的x。

3.加速加密【快速指数算法】

        RSA加密算法的核心在于大数运算,特别是模幂运算,这些运算在加密和解密过程中占据了大量的计算资源。快速指数算法(也称为快速幂算法)是一种高效的计算幂的方法,特别适用于大数的幂运算。

4.加速解密【中国剩余定理】

        中国剩余定理可以将一个大的模幂运算分解为几个较小的模幂运算,从而显著减少计算量,提高解密速度。

(3)算法实现

import random

def MillerRabinTest(n, t):
    if n < 3 or (n & 1 == 0):
        return n == 2
    k, q = 0, n - 1
    # 计算 q 和 k
    while not q & 1:
        q = q // 2
        k = k + 1
    tested = []
    for _ in range(t):
        composite = True
        a = random.randint(2, n - 2)  # 随机生成2~n-2之间的整数
        while a in tested:
            a = random.randint(2, n - 2)
        tested.append(a)
        r = pow(a, q, n)  # 调用快速模幂算法,计算 a^q mod n
        if r == 1 or r == n - 1:
            composite = False
        else:
            # 计算 a^(q·2^j) mod n
            for j in range(1, k):
                r = (r * r) % n
                if r == n - 1:
                    composite = False
                    break
        if composite:
            return False
    return True


def extendedGcd(a, b):
    """
    扩展欧几里得算法
    :return:最大公约数gcd,满足ax+by=gcd的一组解x,y
    """
    if a == 0:
        return b, 0, 1
    gcd, x1, y1 = extendedGcd(b % a, a)
    x = y1 - (b // a) * x1
    y = x1
    return gcd, x, y

def generateKeys(p, q):
    n = p * q
    phi = (p - 1) * (q - 1)

    e = random.randrange(2, phi)
    gcd, x, y = extendedGcd(e, phi)
    while gcd != 1:
        e = random.randrange(2, phi)
        gcd, x, y = extendedGcd(e, phi)

    d = x % phi
    return (e, n), (d, n)

def fastExponentiation(base, exp, mod):
    result = 1
    while exp > 0:
        if exp % 2 == 1:
            result = (result * base) % mod
        base = (base * base) % mod
        exp //= 2
    return result

def rsaEncrypt(plaintext, public_key):
    e, n = public_key
    return fastExponentiation(plaintext, e, n)


def rsaDecrypt(ciphertext, private_key):
    d, n = private_key
    return fastExponentiation(ciphertext, d, n)


def chineseRemainderTheoremDecrypt(ciphertext, p, q, d):
    dp = d % (p - 1)
    dq = d % (q - 1)
    q_inv = pow(q, -1, p)# q^(-1) mod p

    m1 = fastExponentiation(ciphertext, dp, p)# c^dp mod p
    m2 = fastExponentiation(ciphertext, dq, q)# c^dq mod q

    h = (q_inv * (m1 - m2)) % p
    return (m2 + h * q) % (p * q)


if __name__ == '__main__':
    while True:
        x = int(input("请输入模式:1.Miller-Rabin素性测试 2.扩展欧几里得算法计算公私钥 3.快速指数算法实现RSA的加密 4.使用中国剩余定理解密"))
        if x == 1:
            print("----------Miller-Rabin素性测试----------")
            n = int(input("请输入待检测的数:"))
            t = int(input("请输入检测次数:"))
            print(f'{n}是否为素数:{MillerRabinTest(n, t)}')
            print("---------------------------------------")
        elif x == 2:
            print("----------扩展欧几里得算法得到公私钥对----------")
            p = 101
            q = 113
            public_key, private_key = generateKeys(p, q)
            print("公钥对:", public_key)
            print("私钥对:", private_key)
            print("---------------------------------------")
        elif x == 3:
            print("----------快速指数算法实现RSA的加密-----------")
            plaintext = int(input("请输入明文:"))
            p = 101
            q = 113
            public_key = 41, 11413
            ciphertext = rsaEncrypt(plaintext, public_key)
            print("加密结果为:", ciphertext)
            print("---------------------------------------")
        elif x == 4:
            print("---------使用中国剩余定理进行解密-----------")
            private_key = 9561, 11413
            ciphertext = 2596
            p,q=101,113
            decrypted_text_direct = rsaDecrypt(ciphertext, private_key)
            decrypted_text_crt = chineseRemainderTheoremDecrypt(ciphertext, p, q, private_key[0])
            print("直接解密:", decrypted_text_direct)
            print("使用中国剩余定理解密:", decrypted_text_crt)
            print("---------------------------------------")
        else:
            break

古典密码有很多种,比如凯撒密码、简单替换密码、矩阵置换密码等等。这里我以简单替换密码为例,给出Python实现: ```python import random # 生成随机密钥表 def generate_key(): key = list("abcdefghijklmnopqrstuvwxyz") random.shuffle(key) return "".join(key) # 加密函数 def simple_substitution_encrypt(text, key): encrypted_text = "" for char in text: if char.isalpha(): index = ord(char.lower()) - ord('a') encrypted_text += key[index] if char.islower() else key[index].upper() else: encrypted_text += char return encrypted_text # 解密函数 def simple_substitution_decrypt(text, key): return simple_substitution_encrypt(text, key) # 示例 key = generate_key() text = "hello world" encrypted_text = simple_substitution_encrypt(text, key) decrypted_text = simple_substitution_decrypt(encrypted_text, key) print("Key:", key) print("Plaintext:", text) print("Encrypted text:", encrypted_text) print("Decrypted text:", decrypted_text) ``` 其中,`generate_key`函数用于生成一个随机的密钥表,`simple_substitution_encrypt`函数用于加密,`simple_substitution_decrypt`函数用于解密。在示例中,我们生成了一个随机密钥表,然后对字符串"hello world"进行加密和解密。运行结果如下: ``` Key: qyomxvukdbwiczpfhlnjgtraes Plaintext: hello world Encrypted text: itssb hsdit Decrypted text: hello world ``` 需要注意的是,简单替换密码并不安全,因为只需要知道密文和密钥,就可以轻松地破解出明文。实际应用中,需要使用更加复杂的密码算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值