造福下一届,人人有责。

所属实验:西安邮电大学 - 网络空间安全学院 - 密码学基础 - 课程设计实验**

实验时间:2024年上半年

文中所用第三方库使用 pip 自行安装。

另外还有👉信息安全密码学基础 - 实验原理,这里有各个实验的实验原理。

免责声明:实验代码仅供参考,请自行设计实现。

实验一 对称加密

古典密码Hill密码

随机生成一个2×2和一个3×3的可逆的密钥矩阵,矩阵元素取自Z26。

  1. 求这两个矩阵的逆矩阵
  2. 分别用这两个密钥矩阵加密消息(学号+姓名),得密文;解密密文,检验是否正确解密了?
代码 1-1_Symmetric_Hill
import numpy as np
from sympy import Matrix

# 定义可见 ASCII 字符的范围
MIN_ASCII = 32
MAX_ASCII = 126
Z = MAX_ASCII - MIN_ASCII + 1  # 字符集大小

# 生成一个随机的 n x n 可逆矩阵,元素取自 Z
def generate_invertible_matrix(n):
    while True:
        matrix = np.random.randint(0, Z, size=(n, n))
        # 检查矩阵行列式与 Z 是否互质,只有互质时矩阵才是可逆的
        if np.gcd(int(round(np.linalg.det(matrix))), Z) == 1:
            return matrix

# 计算矩阵在模 Z 下的逆矩阵
def modular_inverse(matrix, mod):
    mat = Matrix(matrix)
    # 使用 sympy 库计算矩阵的模逆
    inv_mat = mat.inv_mod(mod)
    return np.array(inv_mat).astype(int)

# 生成 2x2 和 3x3 可逆矩阵
matrix_2x2 = generate_invertible_matrix(2)
matrix_3x3 = generate_invertible_matrix(3)

# 计算矩阵的逆矩阵
inverse_2x2 = modular_inverse(matrix_2x2, Z)
inverse_3x3 = modular_inverse(matrix_3x3, Z)

# 将文本转换为数字列表
def text_to_numbers(text):
    # 将字符的 ASCII 码减去 MIN_ASCII
    return [ord(char) - MIN_ASCII for char in text]

# 将数字列表转换为文本
def numbers_to_text(numbers):
    # 将数字加上 MIN_ASCII 转换回字符
    return ''.join(chr(num + MIN_ASCII) for num in numbers)

# Hill 加密函数
def hill_encrypt(message, key_matrix):
    n = key_matrix.shape[0]
    message_numbers = text_to_numbers(message)
    while len(message_numbers) % n != 0:
        message_numbers.append(0)  # 使用最小的可见 ASCII 字符进行填充

    encrypted_numbers = []
    for i in range(0, len(message_numbers), n):
        block = np.array(message_numbers[i:i + n])
        # 矩阵乘法并取模 Z
        encrypted_block = np.dot(key_matrix, block) % Z
        encrypted_numbers.extend(encrypted_block)

    return numbers_to_text(encrypted_numbers)

# Hill 解密函数
def hill_decrypt(encrypted_message, inverse_key_matrix):
    n = inverse_key_matrix.shape[0]
    encrypted_numbers = text_to_numbers(encrypted_message)

    decrypted_numbers = []
    for i in range(0, len(encrypted_numbers), n):
        block = np.array(encrypted_numbers[i:i + n])
        # 使用逆矩阵进行矩阵乘法并取模 Z
        decrypted_block = np.dot(inverse_key_matrix, block) % Z
        decrypted_numbers.extend(decrypted_block)

    return numbers_to_text(decrypted_numbers).rstrip(chr(0))  # 移除填充

# 实验 ①: 打印生成的矩阵及其逆矩阵
print("实验 ①: 生成的矩阵及其逆矩阵")
print("2x2 矩阵:")
print(matrix_2x2)
print("2x2 逆矩阵:")
print(inverse_2x2)
print("\n3x3 矩阵:")
print(matrix_3x3)
print("3x3 逆矩阵:")
print(inverse_3x3)

# 实验 ②: 加密和解密消息
message = "26221013_Daaihang_Wong"

# 仅保留消息中的可见字符
filtered_message = ''.join(filter(lambda x: MIN_ASCII <= ord(x) <= MAX_ASCII, message))

encrypted_message_2x2 = hill_encrypt(filtered_message, matrix_2x2)
decrypted_message_2x2 = hill_decrypt(encrypted_message_2x2, inverse_2x2)

encrypted_message_3x3 = hill_encrypt(filtered_message, matrix_3x3)
decrypted_message_3x3 = hill_decrypt(encrypted_message_3x3, inverse_3x3)

# 输出结果
print("\n实验 ②: 加密和解密结果")
print("原始消息:", filtered_message)
print("\n使用 2x2 矩阵")
print("加密消息:", encrypted_message_2x2)
print("解密消息:", decrypted_message_2x2)

print("\n使用 3x3 矩阵")
print("加密消息:", encrypted_message_3x3)
print("解密消息:", decrypted_message_3x3)

置换密码

随机生成一个置换表(置换表长度可设置)

  1. 求逆置换
  2. 用此置换表加密消息(学号+姓名),得密文;解密密文,检验是否正确解密了?
代码 1-2_Symmetric_Permutation
import random

# 定义可见 ASCII 字符的范围
MIN_ASCII = 32
MAX_ASCII = 126
CHARS = [chr(i) for i in range(MIN_ASCII, MAX_ASCII + 1)]
LENGTH = len(CHARS)

# 生成随机置换表
def generate_permutation_table(length):
    table = CHARS[:length]
    random.shuffle(table)  # 随机打乱字符列表,形成置换表
    return table

# 生成逆置换表
def generate_inverse_permutation_table(table):
    inverse_table = [''] * len(table)
    for i, char in enumerate(table):
        inverse_table[CHARS.index(char)] = CHARS[i]  # 找到字符在原字符集中的位置,并在逆置换表中记录相应的原字符
    return inverse_table

# 置换加密函数
def permutation_encrypt(message, table):
    # 用置换表替换消息中的每个字符
    return ''.join(table[CHARS.index(char)] if char in CHARS else char for char in message)

# 置换解密函数
def permutation_decrypt(encrypted_message, inverse_table):
    # 用逆置换表还原消息中的每个字符
    return ''.join(inverse_table[CHARS.index(char)] if char in CHARS else char for char in encrypted_message)

# 实验 ①: 打印生成的置换表及其逆置换表
table = generate_permutation_table(LENGTH)
inverse_table = generate_inverse_permutation_table(table)

print("实验 ①: 生成的置换表及其逆置换表")
print("置换表:", ''.join(table))
print("逆置换表:", ''.join(inverse_table))

# 实验 ②: 加密和解密消息
message = "26221013_Daaihang_Wong"
encrypted_message = permutation_encrypt(message, table)
decrypted_message = permutation_decrypt(encrypted_message, inverse_table)

# 输出结果
print("\n实验 ②: 加密和解密结果")
print("原始消息:", message)
print("加密消息:", encrypted_message)
print("解密消息:", decrypted_message)

对称加密算法AES和工作模式

  1. 编写AES加密和解密算法
  2. 分别结合CBC和OFB工作模式,加密消息(学号+姓名),得密文;解密密文,检验是否正确解密了?

我给出了4种方式实现。优先使用第4个。其他的或多或少使用第三方的非基础密码库,可能不符合实验要求。

代码 - 实现一 1-3_Symmetric_AES_1
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os


# 生成随机密钥和初始化向量(IV)
def generate_key_iv(key_size):
    key = os.urandom(key_size // 8)
    iv = os.urandom(16)  # IV 大小应为块大小(16 字节)
    return key, iv


# AES 加密函数
def aes_encrypt(message, key, iv, mode):
    # PKCS7填充
    padder = padding.PKCS7(algorithms.AES.block_size).padder()
    padded_data = padder.update(message.encode()) + padder.finalize()

    # 创建AES加密器
    cipher = Cipher(algorithms.AES(key), mode(iv), backend=default_backend())
    encryptor = cipher.encryptor()

    # 加密数据
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()
    return ciphertext


# AES 解密函数
def aes_decrypt(ciphertext, key, iv, mode):
    # 创建AES解密器
    cipher = Cipher(algorithms.AES(key), mode(iv), backend=default_backend())
    decryptor = cipher.decryptor()

    # 解密数据
    padded_data = decryptor.update(ciphertext) + decryptor.finalize()

    # 去除PKCS7填充
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    decrypted_data = unpadder.update(padded_data) + unpadder.finalize()
    return decrypted_data.decode()


# 主函数
def main():
    # 设置密钥长度(128, 192, 或 256 位)
    key_size = 256

    # 生成密钥和 IV
    key, iv = generate_key_iv(key_size)

    # 打印密钥和 IV
    print("实验 ①: 密钥和 IV")
    print("密钥:", key.hex())
    print("IV:", iv.hex())

    # 定义消息
    message = "26221013_Daaihang_Wong"

    # 实验 ②: CBC 模式加密和解密
    print("\n实验 ②: CBC 模式")
    ciphertext_cbc = aes_encrypt(message, key, iv, modes.CBC)
    print("CBC 加密消息:", ciphertext_cbc.hex())

    decrypted_message_cbc = aes_decrypt(ciphertext_cbc, key, iv, modes.CBC)
    print("CBC 解密消息:", decrypted_message_cbc)

    # 实验 ②: OFB 模式加密和解密
    print("\n实验 ②: OFB 模式")
    ciphertext_ofb = aes_encrypt(message, key, iv, modes.OFB)
    print("OFB 加密消息:", ciphertext_ofb.hex())

    decrypted_message_ofb = aes_decrypt(ciphertext_ofb, key, iv, modes.OFB)
    print("OFB 解密消息:", decrypted_message_ofb)


# 运行主函数
if __name__ == "__main__":
    main()
代码 - 实现二 1-3_Symmetric_AES_2
import os
from pyaes import pyaes


def pad(data):
    padding_needed = 16 - len(data) % 16
    return data + chr(padding_needed) * padding_needed


def split_blocks(data, block_size=16):
    return [data[i:i + block_size] for i in range(0, len(data), block_size)]


def encrypt_cbc(key, iv, data):
    aes = pyaes.AESModeOfOperationCBC(key, iv=iv)
    padded_data = pad(data)
    blocks = split_blocks(padded_data)
    ciphertext = b''.join(aes.encrypt(block) for block in blocks)
    return ciphertext


def unpad(data):
    padding = ord(data[-1])
    return data[:-padding]


def decrypt_cbc(key, iv, ciphertext):
    aes = pyaes.AESModeOfOperationCBC(key, iv=iv)
    blocks = split_blocks(ciphertext)
    plaintext = ''.join(aes.decrypt(block).decode('utf-8') for block in blocks)
    return unpad(plaintext)


def encrypt_ofb(key, iv, data):
    aes = pyaes.AESModeOfOperationOFB(key, iv=iv)
    return aes.encrypt(data)


def decrypt_ofb(key, iv, ciphertext):
    aes = pyaes.AESModeOfOperationOFB(key, iv=iv)
    return aes.decrypt(ciphertext).decode()


def main():
    key = os.urandom(32)
    iv = os.urandom(16)
    print(repr(iv))

    # Using CBC mode
    plaintext_cbc = "26221013_Daaihang_Wong"
    ciphertext_cbc = encrypt_cbc(key, iv, plaintext_cbc)
    print(repr(ciphertext_cbc))

    decrypted_cbc = decrypt_cbc(key, iv, ciphertext_cbc)
    print(decrypted_cbc)
    print(decrypted_cbc == plaintext_cbc)

    # Using OFB mode
    plaintext_ofb = "26221013_Daaihang_Wong"
    ciphertext_ofb = encrypt_ofb(key, iv, plaintext_ofb)
    print(repr(ciphertext_ofb))

    decrypted_ofb = decrypt_ofb(key, iv, ciphertext_ofb)
    print(decrypted_ofb)
    print(decrypted_ofb == plaintext_ofb)


if __name__ == "__main__":
    main()
代码 - 实现三 1-3_Symmetric_AES_3
# 一个简单的 AES-128-ECB,其中 128 指采用 128 位密钥块,
# ECB 模式,为长度不足 128 位的数据块填充 0x00。

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]
]

s_box_inv = [
    [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb],
    [0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb],
    [0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e],
    [0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25],
    [0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92],
    [0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84],
    [0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06],
    [0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b],
    [0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73],
    [0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e],
    [0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b],
    [0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4],
    [0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f],
    [0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef],
    [0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61],
    [0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]
]

rc = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d]


def sub_bytes(grid, inv=False):
    for i, v in enumerate(grid):
        if inv:  # for decryption
            grid[i] = s_box_inv[v >> 4][v & 0xf]
        else:
            grid[i] = s_box[v >> 4][v & 0xf]


def shift_rows(grid, inv=False):
    for i in range(4):
        if inv:  # for decryption
            grid[i::4] = grid[i::4][-i:] + grid[i::4][:-i]
        else:
            grid[i::4] = grid[i::4][i:] + grid[i::4][:i]


def mix_columns(grid):
    def mul_by_2(n):
        s = (n << 1) & 0xff
        if n & 128:
            s ^= 0x1b
        return s

    def mul_by_3(n):
        return n ^ mul_by_2(n)

    def mix_column(c):
        return [
            mul_by_2(c[0]) ^ mul_by_3(c[1]) ^ c[2] ^ c[3],  # [2 3 1 1]
            c[0] ^ mul_by_2(c[1]) ^ mul_by_3(c[2]) ^ c[3],  # [1 2 3 1]
            c[0] ^ c[1] ^ mul_by_2(c[2]) ^ mul_by_3(c[3]),  # [1 1 2 3]
            mul_by_3(c[0]) ^ c[1] ^ c[2] ^ mul_by_2(c[3]),  # [3 1 1 2]
        ]

    for i in range(0, 16, 4):
        grid[i:i + 4] = mix_column(grid[i:i + 4])


def key_expansion(grid):
    for i in range(10 * 4):
        r = grid[-4:]
        if i % 4 == 0:  # 对上一轮最后4字节自循环、S-box置换、轮常数异或,从而计算出当前新一轮最前4字节
            for j, v in enumerate(r[1:] + r[:1]):
                r[j] = s_box[v >> 4][v & 0xf] ^ (rc[i // 4] if j == 0 else 0)

        for j in range(4):
            grid.append(grid[-16] ^ r[j])

    return grid


def add_round_key(grid, round_key):
    for i in range(16):
        grid[i] ^= round_key[i]


def encrypt(b, expanded_key):
    # First round
    add_round_key(b, expanded_key)

    for i in range(1, 10):
        sub_bytes(b)
        shift_rows(b)
        mix_columns(b)
        add_round_key(b, expanded_key[i * 16:])

    # Final round
    sub_bytes(b)
    shift_rows(b)
    add_round_key(b, expanded_key[-16:])
    return b


def decrypt(b, expanded_key):
    # First round
    add_round_key(b, expanded_key[-16:])

    for i in range(9, 0, -1):
        shift_rows(b, True)
        sub_bytes(b, True)
        add_round_key(b, expanded_key[i * 16:])
        for _ in range(3):
            mix_columns(b)

    # Final round
    shift_rows(b, True)
    sub_bytes(b, True)
    add_round_key(b, expanded_key)
    return b


def aes(typ, key, msg):
    expanded = key_expansion(bytearray(key))

    # Pad the message to a multiple of 16 bytes
    b = bytearray(msg)
    if typ == 0:  # only for encryption
        b = bytearray(msg + b'\x00' * (16 - len(msg) % 16))

    # Encrypt/decrypt the message
    for i in range(0, len(b), 16):
        if typ == 0:
            b[i:i + 16] = encrypt(b[i:i + 16], expanded)
        else:
            b[i:i + 16] = decrypt(b[i:i + 16], expanded)
    return bytes(b)


if __name__ == '__main__':
    key = b"Qw3rTyQw3rTyQw3r"
    enc = aes(0, key, b'Daaihang_Wong_26221013')
    dec = aes(1, key, enc)

    print('加密:', enc)
    print('解密:', dec)
代码 - 实现四 1-3_Symmetric_AES_4(推荐参考)
import os

# AES块大小
BLOCK_SIZE = 16


# 生成随机字节
def generate_random_bytes(length):
    return os.urandom(length)


# 填充函数:PKCS#7
def pad(data):
    pad_length = BLOCK_SIZE - len(data) % BLOCK_SIZE
    return data + bytes([pad_length] * pad_length)


# 去填充函数:PKCS#7
def unpad(data):
    pad_length = data[-1]
    return data[:-pad_length]


# 定义S盒和逆S盒
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,
]

inv_s_box = [
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
]


# 字节替换
def sub_bytes(state):
    return bytes(s_box[b] for b in state)


# 字节替换的逆操作
def inv_sub_bytes(state):
    return bytes(inv_s_box[b] for b in state)


# 行移位
def shift_rows(state):
    return bytes([
        state[0], state[5], state[10], state[15],
        state[4], state[9], state[14], state[3],
        state[8], state[13], state[2], state[7],
        state[12], state[1], state[6], state[11],
    ])


# 行移位的逆操作
def inv_shift_rows(state):
    return bytes([
        state[0], state[13], state[10], state[7],
        state[4], state[1], state[14], state[11],
        state[8], state[5], state[2], state[15],
        state[12], state[9], state[6], state[3],
    ])


# GF(2^8)中的乘法
def gmul(a, b):
    p = 0
    for _ in range(8):
        if b & 1:
            p ^= a
        hi_bit_set = a & 0x80
        a <<= 1
        if hi_bit_set:
            a ^= 0x1b
        b >>= 1
    return p & 0xff


# 列混合
def mix_columns(state):
    mixed = bytearray(16)
    for i in range(4):
        mixed[i * 4 + 0] = gmul(state[i * 4 + 0], 2) ^ gmul(state[i * 4 + 1], 3) ^ state[i * 4 + 2] ^ state[i * 4 + 3]
        mixed[i * 4 + 1] = state[i * 4 + 0] ^ gmul(state[i * 4 + 1], 2) ^ gmul(state[i * 4 + 2], 3) ^ state[i * 4 + 3]
        mixed[i * 4 + 2] = state[i * 4 + 0] ^ state[i * 4 + 1] ^ gmul(state[i * 4 + 2], 2) ^ gmul(state[i * 4 + 3], 3)
        mixed[i * 4 + 3] = gmul(state[i * 4 + 0], 3) ^ state[i * 4 + 1] ^ state[i * 4 + 2] ^ gmul(state[i * 4 + 3], 2)
    return bytes(mixed)


# 列混合的逆操作
def inv_mix_columns(state):
    mixed = bytearray(16)
    for i in range(4):
        mixed[i * 4 + 0] = gmul(state[i * 4 + 0], 14) ^ gmul(state[i * 4 + 1], 11) ^ gmul(state[i * 4 + 2], 13) ^ gmul(
            state[i * 4 + 3], 9)
        mixed[i * 4 + 1] = gmul(state[i * 4 + 0], 9) ^ gmul(state[i * 4 + 1], 14) ^ gmul(state[i * 4 + 2], 11) ^ gmul(
            state[i * 4 + 3], 13)
        mixed[i * 4 + 2] = gmul(state[i * 4 + 0], 13) ^ gmul(state[i * 4 + 1], 9) ^ gmul(state[i * 4 + 2], 14) ^ gmul(
            state[i * 4 + 3], 11)
        mixed[i * 4 + 3] = gmul(state[i * 4 + 0], 11) ^ gmul(state[i * 4 + 1], 13) ^ gmul(state[i * 4 + 2], 9) ^ gmul(
            state[i * 4 + 3], 14)
    return bytes(mixed)


# 轮密钥加
def add_round_key(state, round_key):
    return bytes(a ^ b for a, b in zip(state, round_key))


# AES块加密函数
def aes_encrypt_block(block, key):
    state = add_round_key(block, key)
    for _ in range(9):
        state = sub_bytes(state)
        state = shift_rows(state)
        state = mix_columns(state)
        state = add_round_key(state, key)
    state = sub_bytes(state)
    state = shift_rows(state)
    state = add_round_key(state, key)
    return state


# AES块解密函数
def aes_decrypt_block(block, key):
    state = add_round_key(block, key)
    state = inv_shift_rows(state)
    state = inv_sub_bytes(state)
    for _ in range(9):
        state = add_round_key(state, key)
        state = inv_mix_columns(state)
        state = inv_shift_rows(state)
        state = inv_sub_bytes(state)
    state = add_round_key(state, key)
    return state


# CBC模式加密
def aes_cbc_encrypt(key, plaintext):
    iv = generate_random_bytes(BLOCK_SIZE)  # 生成随机初始化向量(IV)
    plaintext_padded = pad(plaintext.encode('utf-8'))
    ciphertext = iv
    previous_block = iv

    for i in range(0, len(plaintext_padded), BLOCK_SIZE):
        plaintext_block = plaintext_padded[i:i + BLOCK_SIZE]
        xor_block = bytes(a ^ b for a, b in zip(plaintext_block, previous_block))
        encrypted_block = aes_encrypt_block(xor_block, key)
        ciphertext += encrypted_block
        previous_block = encrypted_block

    return ciphertext


# CBC模式解密
def aes_cbc_decrypt(key, ciphertext):
    iv = ciphertext[:BLOCK_SIZE]
    actual_ciphertext = ciphertext[BLOCK_SIZE:]
    previous_block = iv
    plaintext_padded = b''

    for i in range(0, len(actual_ciphertext), BLOCK_SIZE):
        ciphertext_block = actual_ciphertext[i:i + BLOCK_SIZE]
        decrypted_block = aes_decrypt_block(ciphertext_block, key)
        plaintext_block = bytes(a ^ b for a, b in zip(decrypted_block, previous_block))
        plaintext_padded += plaintext_block
        previous_block = ciphertext_block

    plaintext = unpad(plaintext_padded)
    return plaintext.decode('utf-8')


# OFB模式加密
def aes_ofb_encrypt(key, plaintext):
    iv = generate_random_bytes(BLOCK_SIZE)  # 生成随机初始化向量(IV)
    plaintext_bytes = plaintext.encode('utf-8')
    ciphertext = iv
    previous_block = iv

    for i in range(0, len(plaintext_bytes), BLOCK_SIZE):
        block = plaintext_bytes[i:i + BLOCK_SIZE]
        encrypted_block = aes_encrypt_block(previous_block, key)
        cipher_block = bytes(a ^ b for a, b in zip(block, encrypted_block))
        ciphertext += cipher_block
        previous_block = encrypted_block

    return ciphertext


# OFB模式解密
def aes_ofb_decrypt(key, ciphertext):
    iv = ciphertext[:BLOCK_SIZE]
    actual_ciphertext = ciphertext[BLOCK_SIZE:]
    previous_block = iv
    plaintext_bytes = b''

    for i in range(0, len(actual_ciphertext), BLOCK_SIZE):
        ciphertext_block = actual_ciphertext[i:i + BLOCK_SIZE]
        encrypted_block = aes_encrypt_block(previous_block, key)
        plaintext_block = bytes(a ^ b for a, b in zip(ciphertext_block, encrypted_block))
        plaintext_bytes += plaintext_block
        previous_block = encrypted_block

    return plaintext_bytes.decode('utf-8')


# 生成AES密钥
def generate_aes_key():
    return generate_random_bytes(BLOCK_SIZE)


def main():
    message = "26221013_Daaihang_Wong"  # 学号+姓名
    print(f"原始消息: {message}")

    # 生成AES密钥
    aes_key = generate_aes_key()

    # CBC模式
    encrypted_message_cbc = aes_cbc_encrypt(aes_key, message)
    print(f"CBC模式加密后的消息: {encrypted_message_cbc}")
    decrypted_message_cbc = aes_cbc_decrypt(aes_key, encrypted_message_cbc)
    print(f"CBC模式解密后的消息: {decrypted_message_cbc}")

    # OFB模式
    encrypted_message_ofb = aes_ofb_encrypt(aes_key, message)
    print(f"OFB模式加密后的消息: {encrypted_message_ofb}")
    decrypted_message_ofb = aes_ofb_decrypt(aes_key, encrypted_message_ofb)
    print(f"OFB模式解密后的消息: {decrypted_message_ofb}")

    # 检查解密后的消息是否正确
    assert message == decrypted_message_cbc, "CBC模式解密失败!"
    assert message == decrypted_message_ofb, "OFB模式解密失败!"
    print("CBC和OFB模式解密成功!")


if __name__ == "__main__":
    main()

实验二 非对称加密

公钥加密算法RSA

  1. 编写RSA密码算法(包含密钥生成、加密和解密)
  2. 利用此算法加密消息(学号+姓名),得密文;解密密文,检验能否正确解密
代码 2-1_Asymmetric_RSA
import random
from sympy import isprime, mod_inverse


class RSAEncryption:
    def __init__(self, key_size=2048):
        self.key_size = key_size
        self.public_key, self.private_key = self.generate_keys()

    def generate_prime(self, n_bits):
        """
        生成一个具有指定位数的素数
        """
        while True:
            num = random.getrandbits(n_bits)
            if isprime(num):
                return num

    def generate_keys(self):
        """
        生成RSA公钥和私钥
        """
        # 选择两个不同的大素数 p 和 q
        p = self.generate_prime(self.key_size // 2)
        q = self.generate_prime(self.key_size // 2)

        # 计算 n = p * q
        n = p * q

        # 计算 φ(n) = (p-1)(q-1),其中φ(n)为n的欧拉函数
        phi = (p - 1) * (q - 1)

        # 选择一个整数 e,使得 1 < e < φ(n) 并且 e 与 φ(n) 互质
        e = 65537  # 常用的 e 值

        # 计算 d 使得 d * e ≡ 1 (mod φ(n)),即 d 是 e 模 φ(n) 的乘法逆元
        d = mod_inverse(e, phi)

        # 公钥: (e, n)
        # 私钥: (d, n)
        public_key = (e, n)
        private_key = (d, n)

        return public_key, private_key

    def encrypt(self, message):
        """
        使用公钥加密消息。将消息转换为整数 m,计算密文 c = m^e mod n。
        """
        e, n = self.public_key
        # 将消息转换为字节,再转换为整数
        message_bytes = message.encode('utf-8')
        message_int = int.from_bytes(message_bytes, byteorder='big')
        # 使用公钥加密消息
        encrypted_message_int = pow(message_int, e, n)
        return encrypted_message_int

    def decrypt(self, encrypted_message_int):
        """
        使用私钥解密消息。计算原文 m = c^d mod n,将整数 m 转换为字节,再转换为字符串。
        """
        d, n = self.private_key
        # 使用私钥解密消息
        decrypted_message_int = pow(encrypted_message_int, d, n)
        # 将整数转换为字节,再转换为字符串
        decrypted_message_bytes = decrypted_message_int.to_bytes((decrypted_message_int.bit_length() + 7) // 8,
                                                                 byteorder='big')
        return decrypted_message_bytes.decode('utf-8')


# 实例化RSAEncryption类
rsa_encryption = RSAEncryption()

# 定义要加密的消息
student_info = "26221013_Daaihang_Wong"

# 加密消息
encrypted_message = rsa_encryption.encrypt(student_info)
print(f"加密消息:{encrypted_message}")

# 解密消息
decrypted_message = rsa_encryption.decrypt(encrypted_message)
print(f"解密消息:{decrypted_message}")

# 验证解密是否正确
if student_info == decrypted_message:
    print("解密成功,消息正确")
else:
    print("解密失败,消息不正确")

公钥加密算法ElGamal

  1. 编写ElGamal加密算法(包含密钥生成、加密和解密过程)
  2. 利用此算法加密消息(学号+姓名),得密文;解密密文,检验能否正确解密
代码 2-2_Asymmetric_ElGamal
from sympy import mod_inverse, isprime, randprime
from random import randint

class ElGamal:
    def __init__(self, key_size=2048):
        self.key_size = key_size
        # 生成一个大素数 p
        self.p = self.generate_large_prime()
        # 生成一个随机整数 g,满足 2 <= g <= p-1
        self.g = randint(2, self.p - 1)
        # 生成私钥 x,满足 1 <= x <= p-2
        self.x = randint(1, self.p - 2)
        # 计算公钥 h = g^x mod p
        self.h = pow(self.g, self.x, self.p)

    def generate_large_prime(self):
        """生成一个大素数"""
        while True:
            # 在指定范围内生成一个随机素数
            prime_candidate = randprime(2 ** (self.key_size - 1), 2 ** self.key_size)
            if isprime(prime_candidate):
                return prime_candidate

    def encrypt(self, message):
        """
        使用公钥加密消息
        步骤:
        1. 生成一个随机整数 y,满足 1 <= y <= p-2
        2. 计算 c1 = g^y mod p
        3. 计算 s = h^y mod p
        4. 将消息转换为整数 m
        5. 计算 c2 = (m * s) mod p
        返回密文对 (c1, c2)
        """
        y = randint(1, self.p - 2)
        c1 = pow(self.g, y, self.p)
        s = pow(self.h, y, self.p)
        m = int.from_bytes(message.encode('utf-8'), 'big')
        c2 = (m * s) % self.p
        return c1, c2

    def decrypt(self, ciphertext):
        """
        使用私钥解密消息
        步骤:
        1. 提取密文对 (c1, c2)
        2. 计算 s = c1^x mod p
        3. 计算 s 的乘法逆元 s_inv = s^-1 mod p
        4. 计算 m = (c2 * s_inv) mod p
        5. 将整数 m 转换为字节,再转换为字符串
        """
        c1, c2 = ciphertext
        s = pow(c1, self.x, self.p)
        s_inv = mod_inverse(s, self.p)
        m = (c2 * s_inv) % self.p
        message = m.to_bytes((m.bit_length() + 7) // 8, 'big').decode('utf-8')
        return message

# 实例化ElGamal类
elgamal = ElGamal()

# 定义要加密的消息
student_info = "26221013_Daaihang_Wong"

# 加密消息
encrypted_message = elgamal.encrypt(student_info)
print(f"加密消息:{encrypted_message}")

# 解密消息
decrypted_message = elgamal.decrypt(encrypted_message)
print(f"解密消息:{decrypted_message}")

# 验证解密是否正确
if student_info == decrypted_message:
    print("解密成功,消息正确")
else:
    print("解密失败,消息不正确")

混合加密(电子信封)

  1. 基于已编写的AES和RSA(或ElGamal)密码算法,实现混合加密
  2. 利用混合算法加密消息(学号+姓名),得密文;解密密文,检验能否正确解密
代码
# todo: 还没实现,先等着吧,太久了就在评论区滴滴我。

实验三 认证技术

注:过程中可以利用密码库中的Hash算法(如SHA2,SM3,SHA3等)

消息认证码(MAC)

  1. 编写基于Hash算法的消息认证码
  2. 利用此算法对消息(学号+姓名)进行完整性和真实性认证
代码 3-1_Certification_MAC
import hashlib


def xor_bytes(a, b):
    """对两个字节序列进行按位异或操作"""
    return bytes(x ^ y for x, y in zip(a, b))


def hmac_hash(key, message, hash_func=hashlib.sha256):
    """
    实现 HMAC 算法
    1. 如果密钥长度大于哈希块大小,则对密钥进行哈希处理
    2. 如果密钥长度小于哈希块大小,则在密钥末尾补零至块大小
    3. 生成内层和外层填充密钥
    4. 使用填充后的密钥和消息进行两次哈希计算
    """
    block_size = hash_func().block_size

    # 如果密钥长度大于块大小,则对密钥进行哈希处理
    if len(key) > block_size:
        key = hash_func(key).digest()

    # 如果密钥长度小于块大小,则在密钥末尾补零至块大小
    if len(key) < block_size:
        key = key.ljust(block_size, b'\0')

    # 生成内层和外层填充密钥
    o_key_pad = xor_bytes(key, b'\x5c' * block_size)
    i_key_pad = xor_bytes(key, b'\x36' * block_size)

    # 内层哈希
    inner_hash = hash_func(i_key_pad + message).digest()
    # 外层哈希
    final_hash = hash_func(o_key_pad + inner_hash).hexdigest()

    return final_hash


def generate_mac(key, message, hash_func=hashlib.sha256):
    """
    生成消息认证码(MAC)
    使用给定的密钥和消息生成 HMAC
    """
    message_bytes = message.encode('utf-8')
    return hmac_hash(key, message_bytes, hash_func)


def verify_mac(key, message, mac_to_verify, hash_func=hashlib.sha256):
    """
    验证消息认证码(MAC)
    比较生成的 MAC 和提供的 MAC 是否相同
    """
    generated_mac = generate_mac(key, message, hash_func)
    return compare_digest(generated_mac, mac_to_verify)


def compare_digest(a, b):
    """比较两个哈希值,防止时间攻击"""
    if len(a) != len(b):
        return False
    result = 0
    for x, y in zip(a, b):
        result |= ord(x) ^ ord(y)
    return result == 0


# 消息
message = "26221013_Daaihang_Wong"

# 使用的密钥
key = b'Qw3rTyQw3rTy'

# 生成消息认证码
mac = generate_mac(key, message)
print(f"消息: {message}")
print(f"生成的MAC: {mac}")

# 验证消息认证码
is_valid = verify_mac(key, message, mac)
print(f"MAC验证结果: {'有效' if is_valid else '无效'}")

# 模拟篡改消息后的MAC验证
tampered_message = "26221013_D@a1han9_W0ng"
tampered_mac = generate_mac(key, tampered_message) # 篡改后的消息的MAC
is_valid_tampered = verify_mac(key, tampered_message, mac)
print(f"篡改后的消息 与 原消息的MAC 验证的结果: {'有效' if is_valid_tampered else '无效'}")

RSA数字签名算法

  1. 编写RSA签名算法(包含密钥生成、签名和验证过程)
  2. 利用此算法对消息(学号+姓名)进行签名;并在接收方进行验证
代码 3-2_Certification_RSA
# RSA数字签名算法(利用密码库中的Hash算法)

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization


def generate_rsa_key_pair():
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048
    )
    public_key = private_key.public_key()
    return private_key, public_key


def sign_message(private_key, message, hash_algorithm):
    signature = private_key.sign(
        message.encode(),
        padding.PSS(
            mgf=padding.MGF1(hash_algorithm),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hash_algorithm
    )
    return signature


def verify_signature(public_key, message, signature, hash_algorithm):
    try:
        public_key.verify(
            signature,
            message.encode(),
            padding.PSS(
                mgf=padding.MGF1(hash_algorithm),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hash_algorithm
        )
        return True
    except Exception as e:
        return False


# 生成密钥对
private_key, public_key = generate_rsa_key_pair()

# 消息
message = "26221013_Daaihang_Wong"

# 使用的哈希算法
hash_algorithm = hashes.SHA256()

# 生成签名
signature = sign_message(private_key, message, hash_algorithm)
print(f"消息: {message}")
print(f"签名: {signature.hex()}")

# 验证签名
is_valid = verify_signature(public_key, message, signature, hash_algorithm)
print(f"签名验证结果: {'有效' if is_valid else '无效'}")

# 模拟篡改消息后的签名验证
tampered_message = "26221013_D@a1han9_W0ng"
is_valid_tampered = verify_signature(public_key, tampered_message, signature, hash_algorithm)
print(f"篡改后消息的签名验证结果: {'有效' if is_valid_tampered else '无效'}")

ElGamal数字签名算法

  1. 编写ElGamal签名算法(包含密钥生成、签名和验证过程)
  2. 利用此算法对消息(学号+姓名)进行签名;并在接收方进行验证
代码 3-3_Certification_ElGamal
import hashlib
import random
from math import gcd
from sympy import mod_inverse, randprime, isprime


# 确保生成元是模p的一个原根
def find_primitive_root(p):
    """
    找到模 p 的一个原根。

    参数:
    p -- 模数,应为素数

    返回:
    g -- 模 p 的一个原根
    """
    if not isprime(p):
        raise ValueError("p must be a prime number")
    phi = p - 1
    prime_factors = set()
    n = phi
    i = 2
    while i * i <= n:
        if n % i == 0:
            prime_factors.add(i)
            while n % i == 0:
                n //= i
        i += 1
    if n > 1:
        prime_factors.add(n)

    for g in range(2, p):
        if all(pow(g, phi // factor, p) != 1 for factor in prime_factors):
            return g
    return None


# ElGamal签名算法类
class ElGamalSignature:
    def __init__(self, p=None, g=None, x=None, bits=256):
        """
        初始化 ElGamal 签名算法对象。

        参数:
        p -- 大素数,模数
        g -- 模 p 的原根
        x -- 私钥
        bits -- 素数 p 的位数,默认为 256

        如果未提供 p、g、x,则随机生成。
        """
        if p is None or g is None or x is None:
            self.p = randprime(2 ** (bits - 1), 2 ** bits)
            self.g = find_primitive_root(self.p)
            self.x = random.randint(1, self.p - 2)
        else:
            self.p = p
            self.g = g
            self.x = x
        self.y = pow(self.g, self.x, self.p)

    # 签名函数
    def sign(self, message):
        """
        对消息进行签名。

        参数:
        message -- 要签名的消息

        返回:
        (r, s) -- 签名结果
        """
        h = hashlib.sha256(message.encode()).digest()
        h_int = int.from_bytes(h, byteorder='big')
        while True:
            k = random.randint(1, self.p - 2)
            if gcd(k, self.p - 1) == 1:  # 确保 k 和 p-1 互质
                break
        r = pow(self.g, k, self.p)
        k_inv = mod_inverse(k, self.p - 1)
        s = (h_int - self.x * r) * k_inv % (self.p - 1)
        return (r, s)

    # 验证函数
    def verify(self, message, signature):
        """
        验证消息的签名是否有效。

        参数:
        message -- 原始消息
        signature -- 签名元组 (r, s)

        返回:
        valid -- 签名是否有效,True 或 False
        """
        r, s = signature
        if not (0 < r < self.p and 0 < s < self.p - 1):  # 检查 r 和 s 的范围
            return False
        h = hashlib.sha256(message.encode()).digest()
        h_int = int.from_bytes(h, byteorder='big')
        v1 = pow(self.y, r, self.p) * pow(r, s, self.p) % self.p
        v2 = pow(self.g, h_int, self.p)
        return v1 == v2


# 测试代码
if __name__ == '__main__':
    # 大素数和生成元
    p = 97380703184954711490740892696023412334547368592870793188226395165275008037663
    g = 2  # 使用find_primitive_root找出的原根,若已知则直接赋值
    x = 123456789  # 私钥

    # 初始化ElGamal签名对象
    elgamal = ElGamalSignature(p, g, x)

    # 消息
    student_info = "26221013_Daaihang_Wong"

    # 签名
    signature = elgamal.sign(student_info)
    print("签名:", signature)

    # 验证
    if elgamal.verify(student_info, signature):
        print("签名有效")
    else:
        print("签名无效")

实验四:密钥管理

DH密钥交换协议

  1. DH密钥交换协议(包含密钥生成、加密和解密)
  2. 双方利用此协议生成的密钥,结合已编写的AES算法来加密消息(学号+姓名),得密文;解密密文,检验能否正确解密
代码 4-1_Key_Management_DH
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os

# 函数:执行Diffie-Hellman密钥交换
def diffie_hellman():
    # 生成Alice的私钥和公钥
    alice_private_key = x25519.X25519PrivateKey.generate()
    alice_public_key = alice_private_key.public_key()

    # 生成Bob的私钥和公钥
    bob_private_key = x25519.X25519PrivateKey.generate()
    bob_public_key = bob_private_key.public_key()

    # Alice计算共享密钥
    alice_shared_key = alice_private_key.exchange(bob_public_key)

    # Bob计算共享密钥
    bob_shared_key = bob_private_key.exchange(alice_public_key)

    # 确保双方计算得到的共享密钥相同
    assert alice_shared_key == bob_shared_key

    return alice_shared_key

# 函数:使用AES加密消息
def encrypt_message(message, key):
    backend = default_backend()
    iv = os.urandom(16)  # 生成随机IV
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=backend)
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return (iv, ciphertext)

# 函数:使用AES解密消息
def decrypt_message(ciphertext, key, iv):
    backend = default_backend()
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=backend)
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext) + decryptor.finalize()
    return plaintext

# 主函数:演示过程
def main():
    # 执行Diffie-Hellman密钥交换
    shared_key = diffie_hellman()
    print(f"共享密钥 (Hex): {shared_key.hex()}")

    # 使用HKDF从共享密钥派生AES密钥
    derived_key = HKDF(
        algorithm=hashes.SHA256(),
        length=32,  # 256位密钥长度
        salt=None,
        info=b'Qw3rTyQw3rTyQw3rTy',
        backend=default_backend()
    ).derive(shared_key)

    # 加密和解密消息
    message = b"26221013_Daaihang_Wong"
    print(f"原始消息: {message.decode()}")

    # 加密消息
    iv, ciphertext = encrypt_message(message, derived_key)
    print(f"加密后的消息 (Hex): {ciphertext.hex()}")

    # 解密消息
    decrypted_message = decrypt_message(ciphertext, derived_key, iv)
    print(f"解密后的消息: {decrypted_message.decode()}")

if __name__ == "__main__":
    main()

Shamir秘密共享方案

  1. 编写一个(n, t) Shamir秘密共享方案(包含秘密的分片和基于Lagrange插值方法恢复秘密)
  2. 实例化(n, t) =(5, 3),而秘密信息为你的“学号+姓名”,演示秘密分片和恢复秘密的过程。
代码 4-2_Key_Management_Shamir
import random
from sympy import nextprime


def generate_shares(secret, n, t, prime):
    """
    使用Shamir的秘密共享方案生成n个分享,阈值为t。

    参数:
    - secret: 要共享的秘密(以数值形式)。
    - n: 要生成的分享总数。
    - t: 恢复秘密所需的分享阈值。
    - prime: 有限域素数,用于计算。

    返回:
    - 一个包含分享的元组列表 (x, y)。
    """
    coefficients = [secret] + [random.randrange(prime) for _ in range(t - 1)]

    def polynomial(x):
        return sum([coeff * pow(x, i, prime) % prime for i, coeff in enumerate(coefficients)]) % prime

    shares = []
    for i in range(1, n + 1):
        x = i
        y = polynomial(x)
        shares.append((x, y))

    return shares


def recover_secret(shares, prime):
    """
    使用Shamir的秘密共享方案恢复秘密,给定足够数量的分享。

    参数:
    - shares: 一个分享的元组列表 (x, y)。
    - prime: 有限域素数,用于计算。

    返回:
    - 恢复的秘密(以数值形式)。
    """

    def lagrange_interpolation(x, shares):
        total = 0
        n = len(shares)
        for i in range(n):
            xi, yi = shares[i]
            term = yi
            for j in range(n):
                if i != j:
                    xj, _ = shares[j]
                    term = term * (x - xj) * pow(xi - xj, -1, prime) % prime
            total = (total + term) % prime
        return total

    return lagrange_interpolation(0, shares)


# 示例:使用学号和姓名作为秘密
secret = "26221013_Daaihang_Wong"
secret_numeric = int.from_bytes(secret.encode(), "big")  # 转换为数值形式

n = 5  # 总分享数
t = 3  # 恢复秘密所需的分享阈值

# 选择一个足够大的素数作为有限域的模数
prime = nextprime(secret_numeric)

# 生成分享
shares = generate_shares(secret_numeric, n, t, prime)

print("分享:")
for share in shares:
    print(share)

# 选择任意 t 个分享来恢复秘密
selected_shares = random.sample(shares, t)

# 恢复秘密
recovered_numeric = recover_secret(selected_shares, prime)

# 将数值形式的秘密转换为字节数据
recovered_secret_bytes = recovered_numeric.to_bytes((recovered_numeric.bit_length() + 7) // 8, "big")

# 比较恢复的秘密与原始秘密
if recovered_secret_bytes == secret.encode():
    print("\n成功恢复秘密:", secret)
else:
    print("\n恢复的秘密与原始秘密不匹配")
版权说明:自由转载-非商用-非衍生-保持署名(CC BY-NC-SA 4.0)