一、实验目的
SM2加密与解密算法与实现
(1)按照有效的椭圆曲线参数生成椭圆曲线
(2)掌握 SM2加密与解密算法原理
(3)将字符串明文转换为比特串格式之间转换,能够将有意义的消息 转换为比特串进行加密,并将解密的结果还原为字符串
硬件:运行 Windows 操作系统的计算机
软件:Python
二、方案设计
背景
SM2算法是中国国家密码管理局(CNCA)发布的一种基于椭圆曲线的非对称加密算法。它采用椭圆曲线密码体系(Elliptic Curve Cryptography,ECC)进行密钥交换、数字签名和公钥加密等功能。SM2算法包括SM2-1椭圆曲线数字签名算法、SM2-2椭圆曲线密钥交换协议和SM2-3椭圆曲线公钥加密算法,分别用于实现数字签名、密钥协商和数据加密等功能。
SM2算法于2010年12月17日首次公开发布,并在2012年成为中国商用密码标准(标准号为GM T 0003—2012),2016年成为中国国家密码标准。该算法的安全性基于椭圆曲线离散对数难题,具有较小的密钥长度、更快的运算速度和更高的安全性,相对于RSA算法具有显著优势。
原理
1.椭圆曲线
椭圆曲线不是椭圆,之所以叫椭圆曲线,是因为其表达式和计算椭圆周长的积分表达式有相似之处,这就是椭圆曲线名称的由来。椭圆周长的积分表达式为:
其中,E(x)是x的三次或四次多项式。
Weierstrass型椭圆曲线是在椭圆曲线密码体制中的一种最常用的曲线:
j称为不变量,当俩条椭圆曲线相同时,则它们同构。
通过变量置换:
得到常用仿射坐标方程简化形式:
判别式为:
2.椭圆曲线离散对数问题(ECDLP)
给定定义在有限域Fp上的椭圆曲线E,及E上的一个n阶点G,和另一点Q,如果存在k,0<k<n-1,使Q=kG,则称k是Q的以G为基的离散对数。
由k和G,求Q容易。
由G和Q,求k困难。
ECC就是建立在求解相应加法群中ECDLP困难基础上的。
3.SM2算法
SM2-算法-密钥生成算法
(1)选择随机整数 。
(2)以G为基点,计算点 。
(3)密钥是,其中d是私钥,P是公钥。
SM2-算法-加密算法
设需要发送的明文为比特串M,klen为M的比特长度。用户A获得用户B的公钥后:
(1)选择随机数 。
(2)计算椭圆曲线点,并将
的数据类型转换为比特串。
(3)计算椭圆曲线点(ℎ为余因子,
为椭圆曲线
的阶,n 是基点G的阶),若S是无穷远点,则报错并退出。
(4)否则计算椭圆曲线点,并将坐标
的数据类型转换为比特
串。
(5)计算,若t为全0比特串,则回退至步骤(1)。
(6)否则计算、
。
(7)输出密文。
SM2-算法-解密算法
用户B用自身的私钥对密文
解密,设klen为密文中
的比特长度:
(1)从C中取出比特串C_1,将C_1的数据类型转换为椭圆曲线上的点,验证是否满足
椭圆曲线方程,若不满足,则报错并退出。
(2)否则计算椭圆曲线点S=[ℎ]C_1,若S是无穷远点,则报错并退出。
(3)否则计算[d_B]C_1=(x_2,y_2),并将坐标x_2、y_2的数据类型转换成比特串。
(4)计算t=KDF(x_2||y_2, klen) ,若t为全0比特串,则报错并退出。
(5)否则从C中取出比特串C_2,计算M^′=C_2⊕t 。
(6)计算u=Hash(x_2||M^′ ||y_2),从C中取出比特串C_3,若u≠C_3,则报错并退出。
(7)否则输出明文M^′=C_2⊕t。
三、方案实现
1.SM2加密算法
1.1 流程图
图1 SM2加密算法实现流程图
1.2 主要函数
def enc():具体实现加密算法。打印所有中间数据,并且实现错误处理等操作。
def keys():随机选择d作为私钥,计算P=dG,作为公钥,返回私钥d和公钥P。
def SM3(message_bytes):接收一串比特串,进行SM3哈希运算,返回哈希结果的字符序列
类型。
def KDF(Z, klen):实现KDF算法,接收(X2,Y2)的比特串,进行SM3_Hash运算,并且只取出
前klen位,用于加密异或。
def is_all_zeros(bitstring):用于检验是否为全0比特串。
def modinv(a, m):实现模逆运算。若模拟不存在,报错。
def add(x1, y1, x2, y2, a, p):实现椭圆曲线上的加法运算。若结果为无穷远点,返回
(0,0),后续做错误处理。
def mul(x1, y1, k, a, p):实现椭圆曲线上的乘法运算。同样,若结果为无穷远点,返回
(0,0),后续做错误处理。
1.3 SM2加密主要代码
import random
import math
import os
import binascii
from gmpy2 import powmod, invert, is_prime
import re
# SM3的初始向量
IV = [
0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
]
# 布尔函数FFj
# SM2的曲线参数
p = 0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff
a = 0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc
b = 0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93
gx = 0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7
gy = 0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0
n = 0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123
E_order = n
def FF(X, Y, Z, j):
if j < 16:
return X ^ Y ^ Z
else:
return (X & Y) | (X & Z) | (Y & Z)
# 置换函数
def P0(X):
return X ^ LS(X, 9) ^ LS(X, 17)
def P1(X):
return X ^ LS(X, 15) ^ LS(X, 23)
# 循环左移
def LS(X, n):
return ((X << n) & 0xFFFFFFFF) | (X >> (32 - n))
# 布尔函数GGj
def GG(X, Y, Z, j):
if j < 16:
return X ^ Y ^ Z
else:
return (X & Y) | (~X & Z)
# 消息填充函数
def pad_message(message):
mlen1 = len(message)
mlen = mlen1
message += b'\x80'
mlen += 1
while mlen % 64 != 56:
message += b'\x00'
mlen += 1
message += (mlen1 * 8).to_bytes(8, 'big')
return message
# SM3压缩函数
def SM3_CF(V, B, k):
W = [0] * 68
W_ = [0] * 64
for i in range(16):
W[i] = int.from_bytes(B[i*4:i*4+4], 'big')
for i in range(16, 68):
W[i] = P1(W[i-16] ^ W[i-9] ^ LS(W[i-3], 15)) ^ LS(W[i-13], 7) ^ W[i-6]
for i in range(64):
W_[i] = W[i] ^ W[i+4]
# for i in range(0, len(W), 4):
# print(' '.join(hex(value) for value in W[i:i+4]))
A, B, C, D, E, F, G, H = V
for i in range(64):
SS1 = LS((LS(A, 12) + E + LS(T(i), i % 32)) & 0xFFFFFFFF, 7)
SS2 = SS1 ^ LS(A, 12)
TT1 = (FF(A, B, C, i) + D + SS2 + W_[i]) & 0xFFFFFFFF
TT2 = (GG(E, F, G, i) + H + SS1 + W[i]) & 0xFFFFFFFF
D = C
C = LS(B, 9)
B = A
A = TT1
H = G
G = LS(F, 19)
F = E
E = P0(TT2)
# print(
# f"Step {i+1} - A: {A:08X}, B: {B:08X}, C: {C:08X}, D: {D:08X}, E: {E:08X}, F: {F:08X}, G: {G:08X}, H: {H:08X}")
return A, B, C, D, E, F, G, H
# T函数
def T(j):
if j < 16:
return 0x79CC4519
else:
return 0x7A879D8A
# SM3哈希函数
def string_to_ascii(s):
return [ord(c) for c in s]
def or_16(A, B):
A = int(A, 16)
B = int(B, 16)
C = A ^ B
C = '{:08x}'.format(C)
return C
# SM3哈希函数
# # 示例
# message = 'abc'
# hash_value = SM3(message)
# formatted_hex_string2 = re.sub(r'(.{8})', r'\1 ', hash_value)
# print("Hash Value:", formatted_hex_string2)
def sm3_hash(message):
return SM3(message)
# def modinv(a, m):
# # 使用扩展欧几里得算法来计算模逆
# def extended_gcd(a, b):
# if a == 0:
# return b, 0, 1
# else:
# gcd, x, y = extended_gcd(b % a, a)
# return gcd, y - (b // a) * x, x
# gcd, x, _ = extended_gcd(a, m)
# if gcd != 1:
# raise ValueError('模逆不存在')
# else:
# return x % m
def modinv(a, m):
def extended_gcd(a, b):
x0, x1, y0, y1 = 1, 0, 0, 1
while b != 0:
q, a, b = a // b, b, a % b
x0, x1 = x1, x0 - q * x1
y0, y1 = y1, y0 - q * y1
return a, x0, y0
gcd, x, _ = extended_gcd(a, m)
if gcd != 1:
raise ValueError('模逆不存在')
else:
return x % m
def add(x1, y1, x2, y2, a, p):
# a是曲线参数,p是选取的素数
if x1 == x2 and y1 != y2:
return 0, 0
# 互为逆元,为无穷远点,舍弃
if x1 == x2 and y1 == y2:
l = (3 * x1 * x1 + a) * modinv(2 * y1, p)
else:
l = (y2 - y1) * modinv(x2 - x1, p)
x3 = l * l - x1 - x2
y3 = l * (x1 - x3) - y1
return x3 % p, y3 % p
def mul(x1, y1, k, a, p):
# a是曲线参数,p是选取的素数
if k == 0:
return 0, 0
if k == 1:
return x1, y1
if k % 2 == 0:
x2, y2 = mul(x1, y1, k // 2, a, p)
return add(x2, y2, x2, y2, a, p)
else:
x2, y2 = mul(x1, y1, (k - 1) // 2, a, p)
return add(x2, y2, x1, y1, a, p)
# 将读入的字符以ascii值转换为8位的比特串,并且返回比特串的长度
def string_to_binary_number(input_string):
# 初始化一个空字符串来存储二进制表示
binary_string = ''
# 遍历输入字符串中的每个字符
for char in input_string:
# 获取字符的ASCII值
ascii_value = ord(char)
# 将ASCII值转换为8位的二进制字符串,并添加到binary_string中
binary_string += format(ascii_value, '08b')
# 将二进制字符串转换为整数
binary_number = int(binary_string, 2)
return binary_number, len(binary_string)
# def SM3(message_bytes):
# # ascii_values = string_to_ascii(message)
# # message_bytes = bytes(ascii_values)
# padded_message = pad_message(message_bytes)
# result = ''
# for i in IV:
# result += '{:08x}'.format(i)
# V = IV.copy()
# for i in range(len(padded_message) // 64):
# B = padded_message[i*64:(i+1)*64]
# V = SM3_CF(V, B, i)
# hex_string1 = binascii.hexlify(B).decode()
# formatted_hex_string1 = re.sub(r'(.{8})', r'\1 ', hex_string1)
# print(
# f"Extended Message at Step {i+1}: {formatted_hex_string1}")
# all = ''
# for iii in V:
# all += '{:08x}'.format(iii)
# result = or_16(all, result)
# # return result
# result_bytes = bytes.fromhex(result)
# return int.from_bytes(result_bytes, byteorder='big')
# def SM3(message_bytes):
# padded_message = pad_message(message_bytes)
# V = IV.copy()
# for i in range(len(padded_message) // 64):
# B = padded_message[i*64:(i+1)*64]
# V = SM3_CF(V, B, i)
# hex_string1 = binascii.hexlify(B).decode()
# formatted_hex_string1 = re.sub(r'(.{8})', r'\1 ', hex_string1)
# print(f"Extended Message at Step {i+1}: {formatted_hex_string1}")
# result = ''.join('{:08x}'.format(v) for v in V)
# result_bytes = bytes.fromhex(result)
# return result_bytes
def SM3(message_bytes):
# ascii_values = string_to_ascii(message)
# message_bytes = bytes(ascii_values)
padded_message = pad_message(message_bytes)
result = ''
for i in IV:
result += '{:08x}'.format(i)
V = IV.copy()
for i in range(len(padded_message) // 64):
B = padded_message[i*64:(i+1)*64]
V = SM3_CF(V, B, i)
hex_string1 = binascii.hexlify(B).decode()
formatted_hex_string1 = re.sub(r'(.{8})', r'\1 ', hex_string1)
# print(
# f"Extended Message at Step {i+1}: {formatted_hex_string1}")
all = ''
for iii in V:
all += '{:08x}'.format(iii)
result = or_16(all, result)
return bytes.fromhex(result)
# def KDF(Z, klen):
# ct = 1
# v = 256 # SM3 输出是256位
# Ha = b""
# for i in range((klen + v - 1) // v): # 向上取整 (klen / v)
# hash_input = Z + ct.to_bytes(4, byteorder='big')
# Ha += SM3(hash_input.to_hex()).to_bytes(32,
# byteorder='big') # SM3 hash to bytes
# ct += 1
# return Ha[:klen // 8] # 只取前 klen 位
def KDF(Z, klen):
ct = 1
v = 256 # SM3 输出是256位
Ha = b""
for i in range((klen + v - 1) // v): # 向上取整 (klen / v)
hash_input = Z + ct.to_bytes(4, byteorder='big')
Ha += SM3(hash_input)
ct += 1
return Ha[:klen // 8] # 只取前 klen 位
def is_all_zeros(bitstring):
# 检查是否是全0序列
return all(b == 0 for b in bitstring)
def format_hex_with_spaces(hex_str):
# 每8个字符插入一个空格
return ' '.join(hex_str[i:i+8] for i in range(0, len(hex_str), 8))
def keys():
d = random.SystemRandom().randrange(1, n-1) # 私钥
# print(f"私钥为:{hex(d)}")
xp, yp = mul(gx, gy, d, a, p)
return d, xp, yp
def enc():
while 1:
# 公钥加密
d = 0xc9b4281d041bf8c51a1d275209f92eba127a439fc20b0b0cbf41d3afbaf17209
# d, xp, yp = keys()
xp, yp = mul(gx, gy, d, a, p)
print(f"私钥为:{hex(d)}")
print(f"公钥横坐标为:{hex(xp)}")
print(f"公钥纵坐标为:{hex(yp)}")
# print(hex(xp))
# print(hex(yp))
# xp=0x9993c9fb227dc53bfa8cdf2724e148147ed00c29c2dfc6537bc12caab946365e
# yp=0xd65eba5e9965e0a4d62328d787d2e4cb72d3b954ace3f2b1232e858924d481a2
# 获取明文,转换为二进制比特串
input_string = input("请输入明文\n")
message, klen = string_to_binary_number(input_string)
message1 = bin(message)
message1 = message1[2:]
print(f"明文比特串为:{message1}\n")
# k = random.SystemRandom().randrange(1, n-1)
k = 0xe1bf10a1ff46073ec16d1eddb6bf5a9c79b58b236d979355b18b1858c255eb75
print(f"随机数k为:{hex(k)}\n")
x1, y1 = mul(gx, gy, k, a, p)
C1 = x1.to_bytes(32, byteorder='big')+y1.to_bytes(32, byteorder='big')
# 将数据类型转化为比特串
# x11 = bin(x1)
# x11 = x11[2:]
print(f"C1的x坐标为:{hex(x1)}\n")
# y11 = bin(y1)
# y11 = y11[2:]
print(f"C1的y坐标为:{hex(y1)}\n")
# 计算余因子
h = E_order // n
xs, ys = mul(gx, gy, h, a, p)
if xs == 0 and ys == 0:
raise ValueError("解密失败,S是无穷远点")
# 验证失败,报错退出
# 成功则进入加密部分
# 计算
x2, y2 = mul(xp, yp, k, a, p)
# x22 = bin(x2)
# y22 = bin(y2)
# x22 = x22[2:]
# y22 = y22[2:]
x2_bytes = x2.to_bytes(32, byteorder='big')
y2_bytes = y2.to_bytes(32, byteorder='big')
print(f"C2的x坐标为:{hex(x2)}\n")
print(f"C2的y坐标为:{hex(y2)}\n")
Z = x2_bytes + y2_bytes
# 调用 KDF 函数
t = KDF(Z, klen)
# 检查 t 是否为全0比特串
if is_all_zeros(t):
print("t 是全0比特串,需要回退并重新执行步骤。\n")
continue
else:
print(f"t = {t.hex()}")
M_bytes = int.to_bytes(message, klen // 8, byteorder='big')
print(f"M_bytes={format_hex_with_spaces(M_bytes.hex())}\n")
C2 = bytes(a ^ b for a, b in zip(M_bytes, t))
C3_input = x2_bytes + M_bytes + y2_bytes
C3 = sm3_hash(C3_input)
# print(f"C2 = {C2.hex()}")
# print(f"C3 = {C3.hex()}")
C1_hex = C1.hex()
C2_hex = C2.hex()
C3_hex = C3.hex()
# print(len(C1_hex))
# print(len(C2_hex))
# print(len(C3_hex))
# 打印格式化后的16进制字符串
print(f"C1 = {format_hex_with_spaces(C1_hex)}")
print(f"C2 = {format_hex_with_spaces(C2_hex)}")
print(f"C3 = {format_hex_with_spaces(C3_hex)}")
C = C1_hex+C2_hex+C3_hex
print(len(C))
print(f"C = {format_hex_with_spaces(C)}")
break
# 密文C 297f0d23 11858162 7d49846a 5fa4511c 13ba67d6 f9493c9f 9f897f61 dc258def f2ba9f9a 693dd185 33b93495 e9a2da9f 7ec7961a d7ca947f 4ea9e81d 47bfaf3a a12eb6d1 4e6e9d3f 23472a1a 33f13ca1 646c75f9
try:
enc()
except ValueError as e:
print(e)
2.SM2解密算法
2.1 流程图
图2 SM2解密算法实现流程图
2.2 主要函数
def dec(C, d, a, p, mul, KDF, is_all_zeros, sm3_hash):具体实现解密算法,打印所有中间数据,并且实现错误处理等操作。
其他函数与加密基本一致。
2.3 SM2解密主要代码
import random
import math
import os
import binascii
from gmpy2 import powmod, invert, is_prime
import re
# SM3的初始向量
IV = [
0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
]
# 布尔函数FFj
# SM2的曲线参数
p = 0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff
a = 0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc
b = 0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93
gx = 0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7
gy = 0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0
n = 0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123
E_order = n
# 椭圆曲线点加法
def FF(X, Y, Z, j):
if j < 16:
return X ^ Y ^ Z
else:
return (X & Y) | (X & Z) | (Y & Z)
# 置换函数
def P0(X):
return X ^ LS(X, 9) ^ LS(X, 17)
def P1(X):
return X ^ LS(X, 15) ^ LS(X, 23)
# 循环左移
def LS(X, n):
return ((X << n) & 0xFFFFFFFF) | (X >> (32 - n))
# 布尔函数GGj
def GG(X, Y, Z, j):
if j < 16:
return X ^ Y ^ Z
else:
return (X & Y) | (~X & Z)
# 消息填充函数
def pad_message(message):
mlen1 = len(message)
mlen = mlen1
message += b'\x80'
mlen += 1
while mlen % 64 != 56:
message += b'\x00'
mlen += 1
message += (mlen1 * 8).to_bytes(8, 'big')
return message
# SM3压缩函数
def SM3_CF(V, B, k):
W = [0] * 68
W_ = [0] * 64
for i in range(16):
W[i] = int.from_bytes(B[i*4:i*4+4], 'big')
for i in range(16, 68):
W[i] = P1(W[i-16] ^ W[i-9] ^ LS(W[i-3], 15)) ^ LS(W[i-13], 7) ^ W[i-6]
for i in range(64):
W_[i] = W[i] ^ W[i+4]
# for i in range(0, len(W), 4):
# print(' '.join(hex(value) for value in W[i:i+4]))
A, B, C, D, E, F, G, H = V
for i in range(64):
SS1 = LS((LS(A, 12) + E + LS(T(i), i % 32)) & 0xFFFFFFFF, 7)
SS2 = SS1 ^ LS(A, 12)
TT1 = (FF(A, B, C, i) + D + SS2 + W_[i]) & 0xFFFFFFFF
TT2 = (GG(E, F, G, i) + H + SS1 + W[i]) & 0xFFFFFFFF
D = C
C = LS(B, 9)
B = A
A = TT1
H = G
G = LS(F, 19)
F = E
E = P0(TT2)
# print(
# f"Step {i+1} - A: {A:08X}, B: {B:08X}, C: {C:08X}, D: {D:08X}, E: {E:08X}, F: {F:08X}, G: {G:08X}, H: {H:08X}")
return A, B, C, D, E, F, G, H
# T函数
def T(j):
if j < 16:
return 0x79CC4519
else:
return 0x7A879D8A
# SM3哈希函数
def string_to_ascii(s):
return [ord(c) for c in s]
def or_16(A, B):
A = int(A, 16)
B = int(B, 16)
C = A ^ B
C = '{:08x}'.format(C)
return C
# SM3哈希函数
# # 示例
# message = 'abc'
# hash_value = SM3(message)
# formatted_hex_string2 = re.sub(r'(.{8})', r'\1 ', hash_value)
# print("Hash Value:", formatted_hex_string2)
def sm3_hash(message):
return SM3(message)
# def modinv(a, m):
# # 使用扩展欧几里得算法来计算模逆
# def extended_gcd(a, b):
# if a == 0:
# return b, 0, 1
# else:
# gcd, x, y = extended_gcd(b % a, a)
# return gcd, y - (b // a) * x, x
# gcd, x, _ = extended_gcd(a, m)
# if gcd != 1:
# raise ValueError('模逆不存在')
# else:
# return x % m
def modinv(a, m):
def extended_gcd(a, b):
x0, x1, y0, y1 = 1, 0, 0, 1
while b != 0:
q, a, b = a // b, b, a % b
x0, x1 = x1, x0 - q * x1
y0, y1 = y1, y0 - q * y1
return a, x0, y0
gcd, x, _ = extended_gcd(a, m)
if gcd != 1:
raise ValueError('模逆不存在')
else:
return x % m
def add(x1, y1, x2, y2, a, p):
if x1 == x2 and y1 != y2:
return 0, 0
if x1 == x2 and y1 == y2:
l = (3 * x1 * x1 + a) * modinv(2 * y1, p)
else:
l = (y2 - y1) * modinv(x2 - x1, p)
x3 = l * l - x1 - x2
y3 = l * (x1 - x3) - y1
return x3 % p, y3 % p
# 椭圆曲线点乘法
def mul(x1, y1, k, a, p):
if k == 0:
return 0, 0
if k == 1:
return x1, y1
if k % 2 == 0:
x2, y2 = mul(x1, y1, k // 2, a, p)
return add(x2, y2, x2, y2, a, p)
else:
x2, y2 = mul(x1, y1, (k - 1) // 2, a, p)
return add(x2, y2, x1, y1, a, p)
# KDF函数
def KDF(Z, klen):
ct = 1
v = 256 # SM3 输出是256位
Ha = b""
for i in range((klen + v - 1) // v): # 向上取整 (klen / v)
hash_input = Z + ct.to_bytes(4, byteorder='big')
Ha += SM3(hash_input)
ct += 1
return Ha[:klen // 8] # 只取前 klen 位
# 判断是否为全0比特串
def is_all_zeros(bitstring):
return all(b == 0 for b in bitstring)
# def SM3(message_bytes):
# padded_message = pad_message(message_bytes)
# V = IV.copy()
# for i in range(len(padded_message) // 64):
# B = padded_message[i*64:(i+1)*64]
# V = SM3_CF(V, B, i)
# hex_string1 = binascii.hexlify(B).decode()
# formatted_hex_string1 = re.sub(r'(.{8})', r'\1 ', hex_string1)
# print(f"Extended Message at Step {i+1}: {formatted_hex_string1}")
# result = ''.join('{:08x}'.format(v) for v in V)
# result_bytes = bytes.fromhex(result)
# return result_bytes
def SM3(message_bytes):
# ascii_values = string_to_ascii(message)
# message_bytes = bytes(ascii_values)
padded_message = pad_message(message_bytes)
result = ''
for i in IV:
result += '{:08x}'.format(i)
V = IV.copy()
for i in range(len(padded_message) // 64):
B = padded_message[i*64:(i+1)*64]
V = SM3_CF(V, B, i)
hex_string1 = binascii.hexlify(B).decode()
formatted_hex_string1 = re.sub(r'(.{8})', r'\1 ', hex_string1)
# print(
# f"Extended Message at Step {i+1}: {formatted_hex_string1}")
all = ''
for iii in V:
all += '{:08x}'.format(iii)
result = or_16(all, result)
return bytes.fromhex(result)
# 解密函数
def format_hex_with_spaces(hex_str):
# 每8个字符插入一个空格
return ' '.join(hex_str[i:i+8] for i in range(0, len(hex_str), 8))
def dec(C, d, a, p, mul, KDF, is_all_zeros, sm3_hash):
while 1:
# 字节少2倍
C1 = C[:64]
C2 = C[64:-32]
C3 = C[-32:]
C1_hex = C1.hex()
C2_hex = C2.hex()
C3_hex = C3.hex()
# print(len(C1_hex))
# print(len(C2_hex))
# print(len(C3_hex))
# 打印格式化后的16进制字符串
print('\n')
print(f"C1 = {format_hex_with_spaces(C1_hex)}")
print(f"C2 = {format_hex_with_spaces(C2_hex)}")
print(f"C3 = {format_hex_with_spaces(C3_hex)}")
x1 = int.from_bytes(C1[:32], byteorder='big')
y1 = int.from_bytes(C1[32:], byteorder='big')
# 验证x1盒y1是否满足是椭圆曲线上的点,不满足则报错退出
# if y1*y1 % p != (x1*x1*x1 + a*x1 + 1) % p:
# raise ValueError("解密失败,C1不是椭圆曲线上的点")
# 验证C1
# X1
# 79377281213374464856521762040730683342015462370049267023955583132112479632217
# Y1
# 29695191565483698076752985044876734951232190773968731139074079091268997953382
print(f"C1的x坐标为:{hex(x1)}\n")
print(f"C1的y坐标为:{hex(y1)}\n")
h = E_order // n
xs, ys = mul(gx, gy, h, a, p)
if xs == 0 and ys == 0:
raise ValueError("解密失败,S是无穷远点")
# 验证失败,回到第一步
x2, y2 = mul(x1, y1, d, a, p)
Z = x2.to_bytes(32, byteorder='big') + y2.to_bytes(32, byteorder='big')
print(f"C2的x坐标为:{hex(x2)}\n")
print(f"C2的y坐标为:{hex(y2)}\n")
# Z
# b'\xf0\xef\xa2c\x05{l:\xa5\xd73\xa9\x83N<\x06?\x1bu\xee:J\xd2N\x1fMm\x98\r\xc0\xb3\xb2\\\x07\x9a\x16\x04\x14\x17\xef\x11Q\xea\x1a3\x80\xa4\x0f\x8d\xa5p\x8e\xbf\x95\x7f\x1e\x00m\xe2\x13\xf7\xb9\xf6\xac'
# b'\xf0\xef\xa2c\x05{l:\xa5\xd73\xa9\x83N<\x06?\x1bu\xee:J\xd2N\x1fMm\x98\r\xc0\xb3\xb2\\\x07\x9a\x16\x04\x14\x17\xef\x11Q\xea\x1a3\x80\xa4\x0f\x8d\xa5p\x8e\xbf\x95\x7f\x1e\x00m\xe2\x13\xf7\xb9\xf6\xac'
# t
# b'h\xd6\xf9\x11y\x00\x81\x04t\xec\x7f\x88BHE\xb7\xb0\xde\x88\xbd'
# b'h\xd6\xf9\x11y\x00\x81\x04t\xec\x7f\x88BHE\xb7\xb0\xde\x88\xbd\xfe\xae\x06\xd4\xcd\xac&\x13\x13\xd3\x0c\xfe@\xa7_\xde\xe5q\xe8\x0c\xafx~\xad\xb0\xaa\xb0vpM\xf8_'
# 密文
# af7df2f5 03d2e3e5 cb91003b 0bf53e67 c177009c e2bee109 80e1bd95 5ae4bb59 41a6e200 015d8cb2 cf52b3f9 cb5894a8 15e86bae f995620e 6b12e20c 903d3766 21f6957e 0f65a167 06950ffc 2d2f37d6 c0b6f193 43d8573f d59a6167 f4b7cad3 7bcd9d7c cc77dfc6 e5a2c2d3 dfcae83c 61d28d5e
klen = len(C2) * 8
t = KDF(Z, klen)
if is_all_zeros(t):
raise ValueError("解密失败,t是全0比特串")
# message, klen = string_to_binary_number(input_string)
# M_bytes = int.to_bytes(message, klen // 8, byteorder='big')
# # print(len(M_bytes))
# C2 = bytes(a ^ b for a, b in zip(M_bytes, t))
# C3_input = x2_bytes + M_bytes + y2_bytes
else:
print(f"t = {t.hex()}")
M_bytes = bytes(a ^ b for a, b in zip(C2, t))
C3_input = x2.to_bytes(32, byteorder='big') + \
M_bytes + y2.to_bytes(32, byteorder='big')
C3_calculated = sm3_hash(C3_input)
if C3 != C3_calculated:
raise ValueError("解密失败,C3验证不通过")
return M_bytes.decode('utf-8')
# 密钥已知
d = 0xc9b4281d041bf8c51a1d275209f92eba127a439fc20b0b0cbf41d3afbaf17209
print(f"私钥d:{d}")
# 从键盘输入密文
C_hex_with_spaces = input("请输入密文的十六进制表示: ")
# C c6bfd950 146bd8be c5264ecf 85db11e7 1526a4fd b574252a 0a777362 b4e025af 35d17811 629b9055 2be2df87 a36891ef 4363456b 1a8314c8 e5326381 fc53401b caed0e6f 5759d193 501c4f73 bed9bab6 9ff21838 bc46202d cfb92c23 d034f18c be88a9ef 90322711 953ccdac 590e3051 c29ce38b
# 移除输入中的空格
C_hex = C_hex_with_spaces.replace(" ", "")
# 将输入的十六进制转换为字节
C = bytes.fromhex(C_hex)
# 调用解密函数
try:
decrypted_text = dec(C, d, a, p, mul, KDF, is_all_zeros, sm3_hash)
print(f"解密后的明文: {decrypted_text}")
except ValueError as e:
print(e)
四、数据分析
1.密钥生成
图3 SM2算法密钥生成
2.加密
明文:I love cryptography.
输入明文,转换为比特串:
图4 SM2加密算法明文转换为比特串
生成随机数k:
图5 SM2加密算法随机选取数k
计算坐标:
图6 SM2加密算法C1的坐标
计算坐标:
图7 SM2加密算法C1的坐标
整合后,通过KDF算法计算t ,
图8 SM2加密算法t的计算结果
加密结果,,以及最终密文
。
图9 SM2加密算法密文结果
3.解密
接收密文,将它分解为
图10 SM2解密算法拆分密文结果
与加密结果对照,拆分正确。
计算坐标:
图11 SM2解密算法计算C1坐标结果
与加密过程对照,计算正确。
计算坐标:
图12 SM2解密算法计算C2坐标结果
与加密过程对照,计算正确。
整合后,通过KDF算法计算t ,
图13 SM2解密算法t的计算结果
解密密文,并且转换为字符串输出:
图14 SM2解密算法解密结果
与原文对照,解密成功。
五、思考与总结
1.实验过程中遇到了什么问题,如何解决的?
问题1:求逆的扩展欧几里得算法一直出错。
原因:数据过大,导致递归溢出。
方案:改用非递归算法解决。
问题2:字节序列和16进制序列存在转换关系。
方案:字节序列对应两个字节,存在2倍关系,在解密分解密文时,注意读取下标。
问题3:整合数据时,只整合了x坐标,而且解密时,未y的坐标,导致对于明文消息的异或
出现问题。
方案:将的x和y坐标都转化为32位的比特序列,解密时按位提取。
2.通过该实验有何收获和感想?
收获:
椭圆曲线参数的生成
通过本次实验,我学会了如何根据给定的有效参数(如素数域、基点、阶等)利用数学工具软件或编程库来生成椭圆曲线。这一步骤是SM2加密算法的基础,因为椭圆曲线的性质直接影响到加密的安全性。
SM2加密与解密算法原理的理解
在理论学习阶段,我详细研究了SM2算法的工作机制,包括密钥对的生成、加密过程中的点乘运算、解密时私钥的应用等。通过编写代码实现这些算法,我不仅加深了对SM2加密算法的认识,还体会到了公钥密码体系中密钥管理的重要性。
感想:
本次实验不仅让我对SM2加密算法有了更深刻的认识,也锻炼了我的编程能力和问题解决能力。在实际操作中遇到的难题,如参数选择的合理性、编码转换的准确性以及加密解密过程的可靠性,都促使我不断寻求解决方案,增强了我的实验技能和理论知识的应用能力。