一、实验要求与目的
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);
计算效率更高,尤其适合移动端和资源受限设备;
国密标准要求的重要算法之一,在国产密码应用中具有推广意义。