【应用密码学】实验六 公钥密码3——SM2

发布于:2025-05-21 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、实验要求与目的

1.复习公钥加密基本概念与RSA,ElGamal,ECC方案的基本原理

2.编程练习:编写SM2加密算法。

二、实验内容与步骤记录(只记录关键步骤与结果,可截图,但注意排版与图片大小)

2.1 椭圆曲线参数选取与点生成

选取椭圆曲线形式:

在实验中使用较小素数 p=751,参数 a=−1,b=188,用于构建有限域内的测试曲线。

通过遍历 x∈[0,p),求解对应合法的 y 使得 y2≡x3+ax+b(modp),筛选出所有椭圆曲线上的有效点,作为后续点运算的基础。

2.2 实现椭圆曲线核心运算模块

点加法:实现通用的两点加法操作,包括同点加、自反点加等边界情况处理;

点倍乘:通过“二进制展开”方式进行点的多倍计算,实现 kP;

点逆元:求解给定点的负元 −P=(x,−y mod  p)。

这些操作构成 SM2 加解密过程中对椭圆曲线的基本支持。

2.3 SM2 密钥生成过程

用户输入:

生成元点 GGG(选自上述曲线上的合法点);

私钥 d∈[1,n−1]。

通过点倍乘计算公钥 P=dG。

2.4 SM2 加密实现流程(使用真实 SM3)

输入明文字符串 M = "Hello SM2 + SM3!",并选择随机整数 k,执行如下流程:

计算 C1 = kG;

计算临时密钥点 (x2, y2) = kP;

使用 SM3 构造 KDF 密钥派生函数:

 

加密明文:

 

消息摘要生成:

输出密文三元组 (C1,C2,C3)。

此过程严格符合《GM/T 0003.1-2012 SM2公钥加密算法》的国密要求。

2.5 SM3 哈希函数自定义实现

相比于 Python 自带的 hashlib.sha256,本实验实现了完整符合 GM/T 0004-2012标准的 SM3 哈希函数,包括:

填充与分组;消息扩展(W, W′);压缩函数迭代(64轮);输出256位摘要结果。

SM3 用于 SM2 加密中的 完整性验证(C3) 和 密钥派生函数(KDF),是该加密算法的必要组成部分。

2.6 最终运行结果输出

完整运行后,控制台将输出:

椭圆曲线点集;私钥、公钥、生成元;明文消息;随机数 k;

加密结果:C1:椭圆曲线上的密钥点;C2:异或加密密文;C3:SM3摘要结果。

所有输出均符合加密算法设计预期。

实验结果如下:

三、源代码记录(关键代码需备注)

import struct

# ---------------- SM3 哈希函数(真实国密标准实现) ---------------- #
IV = [
    0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
    0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
]
T_j = [0x79CC4519] * 16 + [0x7A879D8A] * 48

def _left_rotate(x, n):
    n = n % 32  # 防止位移为负或超出范围
    return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

def _FF_j(x, y, z, j):
    return x ^ y ^ z if j < 16 else (x & y) | (x & z) | (y & z)

def _GG_j(x, y, z, j):
    return x ^ y ^ z if j < 16 else (x & y) | (~x & z)

def _P0(x):
    return x ^ _left_rotate(x, 9) ^ _left_rotate(x, 17)

def _P1(x):
    return x ^ _left_rotate(x, 15) ^ _left_rotate(x, 23)

def _padding(message: bytes) -> bytes:
    l = len(message) * 8
    message += b'\x80'
    while (len(message) * 8 + 64) % 512 != 0:
        message += b'\x00'
    message += struct.pack('>Q', l)
    return message

def _message_extension(B: bytes):
    W = [int.from_bytes(B[i:i+4], 'big') for i in range(0, 64, 4)]
    for i in range(16, 68):
        W.append(_P1(W[i-16] ^ W[i-9] ^ _left_rotate(W[i-3], 15)) ^ _left_rotate(W[i-13], 7) ^ W[i-6])
    W_ = [W[i] ^ W[i+4] for i in range(64)]
    return W, W_

def _CF(V_i, B_i):
    A, B, C, D, E, F, G, H = V_i
    W, W_ = _message_extension(B_i)

    for j in range(64):
        SS1 = _left_rotate((_left_rotate(A, 12) + E + _left_rotate(T_j[j], j)) & 0xFFFFFFFF, 7)
        SS2 = SS1 ^ _left_rotate(A, 12)
        TT1 = (_FF_j(A, B, C, j) + D + SS2 + W_[j]) & 0xFFFFFFFF
        TT2 = (_GG_j(E, F, G, j) + H + SS1 + W[j]) & 0xFFFFFFFF
        D = C
        C = _left_rotate(B, 9)
        B = A
        A = TT1
        H = G
        G = _left_rotate(F, 19)
        F = E
        E = _P0(TT2)

    return [
        A ^ V_i[0], B ^ V_i[1], C ^ V_i[2], D ^ V_i[3],
        E ^ V_i[4], F ^ V_i[5], G ^ V_i[6], H ^ V_i[7]
    ]

def sm3_hash(msg: bytes) -> bytes:
    msg = _padding(msg)
    V = IV.copy()
    for i in range(0, len(msg), 64):
        B_i = msg[i:i+64]
        V = _CF(V, B_i)
    return b''.join(v.to_bytes(4, 'big') for v in V)

# ---------------- 椭圆曲线操作复用你的函数 ---------------- #
def point_addition(p, a, P, Q):
    if P == (0, 0): return Q
    if Q == (0, 0): return P
    if P == Q:
        if P[1] == 0: return (0, 0)
        numerator = (3 * P[0]**2 + a) % p
        denominator = (2 * P[1]) % p
    else:
        if P[0] == Q[0] and (P[1] + Q[1]) % p == 0:
            return (0, 0)
        numerator = (Q[1] - P[1]) % p
        denominator = (Q[0] - P[0]) % p
    lam = (numerator * pow(denominator, p - 2, p)) % p
    x3 = (lam**2 - P[0] - Q[0]) % p
    y3 = (lam * (P[0] - x3) - P[1]) % p
    return (x3, y3)

def point_multiplication(p, a, P, k):
    result = (0, 0)
    temp = P
    while k > 0:
        if k & 1:
            result = point_addition(p, a, result, temp)
        temp = point_addition(p, a, temp, temp)
        k >>= 1
    return result

# ---------------- SM2 加密函数 ---------------- #
def int_to_bytes(n: int, length: int = 32) -> bytes:
    return n.to_bytes(length, 'big')

def KDF(Z: bytes, klen: int) -> bytes:
    ct = 1
    K = b''
    while len(K) < klen:
        K += sm3_hash(Z + ct.to_bytes(4, 'big'))
        ct += 1
    return K[:klen]

def str_to_bytes(msg: str) -> bytes:
    return msg.encode('utf-8')

def SM2_encrypt(p, a, G, public_key, plaintext: str, k: int):
    M = str_to_bytes(plaintext)
    kG = point_multiplication(p, a, G, k)
    kPB = point_multiplication(p, a, public_key, k)
    x2, y2 = kPB
    x2_bytes = int_to_bytes(x2)
    y2_bytes = int_to_bytes(y2)

    t = KDF(x2_bytes + y2_bytes, len(M))
    if all(b == 0x00 for b in t):
        raise ValueError("KDF output all zero, retry with different k")

    C2 = bytes([m ^ t_i for m, t_i in zip(M, t)])
    C3 = sm3_hash(x2_bytes + M + y2_bytes)

    return kG, C3, C2

def print_ciphertext(C1, C3, C2):
    print("密文 C1 (椭圆曲线点):", C1)
    print("密文 C3 (SM3摘要):", C3.hex())
    print("密文 C2 (密文数据):", C2.hex())

# ---------------- 示例 ---------------- #
if __name__ == "__main__":
    p = 751
    a = -1
    b = 188
    G = (0, 376)
    d = 45
    P = point_multiplication(p, a, G, d)

    msg = "Hello SM2 + SM3!"
    k = 20

    print("明文:", msg)
    print("使用随机数 k =", k)
    print("公钥 P =", P)

    C1, C3, C2 = SM2_encrypt(p, a, G, P, msg, k)
    print_ciphertext(C1, C3, C2)

四、实验思考

1. SM2 ECC 的关系?

SM2 是基于 ECC 椭圆曲线密码体制之上的国密算法标准,其核心算法结构与 ECC 类似,但对加密过程中的 KDF 派生函数与哈希校验(C3)进行了国密定制。

2. SM2 的安全性基于什么?

其安全性依赖于**椭圆曲线离散对数问题(ECDLP**的计算困难性,即在给定 Q=dGQ = dGQ=dG 的前提下,难以从 QQQ 推算出私钥 ddd。目前在经典计算模型下,没有有效的多项式时间算法能解决该问题。

3. RSA 相比,ECC/SM2 的优势?

更短的密钥长度即可实现同等安全性(256 ECC ≈ 3072 RSA);

计算效率更高,尤其适合移动端和资源受限设备;

国密标准要求的重要算法之一,在国产密码应用中具有推广意义。


网站公告

今日签到

点亮在社区的每一天
去签到