SM2椭圆曲线公钥密码算法

一、实验目的

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.椭圆曲线

        椭圆曲线不是椭圆,之所以叫椭圆曲线,是因为其表达式和计算椭圆周长的积分表达式有相似之处,这就是椭圆曲线名称的由来。椭圆周长的积分表达式为:

\int \frac{dx}{\sqrt{E(x)}}

其中,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)选择随机整数  d\in [1,n-2] 。

(2)以G为基点,计算点  P=(x_{P},y_{P})=[d]G

(3)密钥是(d,P),其中d是私钥,P是公钥。

SM2-算法-加密算法

设需要发送的明文为比特串M,klen为M的比特长度。用户A获得用户B的公钥P_{B}后:

                (1)选择随机数  k\in[1,n-1]

                (2)计算椭圆曲线点C_{1}=[k]G=(x_{1},y_{1}),并将C_{1}的数据类型转换为比特串。

                (3)计算椭圆曲线点S=[h]P_{B}(ℎ为余因子,h=\varphi E(F_{q})/n,\varphi E(F_{q})为椭圆曲线

                         E(F_{q}) 的阶,n 是基点G的阶),若S是无穷远点,则报错并退出。

                (4)否则计算椭圆曲线点[k]P_{B}=(x_{2},y_{2}),并将坐标(x_{2},y_{2})的数据类型转换为比特

                        串。

                (5)计算t=KDF(x_{2}||y_{2},klen),若t为全0比特串,则回退至步骤(1)。

                (6)否则计算C_{2}=M\bigoplus tC_{3}=Hash(x_{2}||M||y_{2}) 。

                (7)输出密文C=C_{1}||C_{2}||C_{3}

SM2-算法-解密算法

用户B用自身的私钥d_{B}对密文C=C_{1}||C_{2}||C_{3}解密,设klen为密文中C_{2}的比特长度:

                (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

计算C_{1}坐标: 

6 SM2加密算法C1的坐标

计算C_{2}坐标: 

       图7 SM2加密算法C1的坐标

整合(x_{2},y_{2})后,通过KDF算法计算t ,

8 SM2加密算法t的计算结果

加密结果,C_{1},C_{​{2}},C_{3},以及最终密文C 。 

9 SM2加密算法密文结果 

3.解密 

接收密文C,将它分解为C_{1},C_{​{2}},C_{3}

10 SM2解密算法拆分密文结果 

 与加密结果对照,拆分正确。

计算C_{1}坐标: 

11 SM2解密算法计算C1坐标结果 

与加密过程对照,计算正确。 

计算C_{2}坐标:

12 SM2解密算法计算C2坐标结果

 与加密过程对照,计算正确。

整合(x_{2},y_{2})后,通过KDF算法计算t ,

13 SM2解密算法t的计算结果 

解密密文,并且转换为字符串输出: 

14 SM2解密算法解密结果

与原文对照,解密成功。 

五、思考与总结

1.实验过程中遇到了什么问题,如何解决的?

        问题1:求逆的扩展欧几里得算法一直出错。

        原因:数据过大,导致递归溢出。

        方案:改用非递归算法解决。

        问题2:字节序列和16进制序列存在转换关系。

        方案:字节序列对应两个字节,存在2倍关系,在解密分解密文时,注意读取下标。

        问题3:整合数据C_{1}时,只整合了x坐标,而且解密时,未y的坐标,导致对于明文消息的异或

                出现问题。

        方案:C_{1}的x和y坐标都转化为32位的比特序列,解密时按位提取。

2.通过该实验有何收获和感想?

收获:

椭圆曲线参数的生成

        通过本次实验,我学会了如何根据给定的有效参数(如素数域、基点、阶等)利用数学工具软件或编程库来生成椭圆曲线。这一步骤是SM2加密算法的基础,因为椭圆曲线的性质直接影响到加密的安全性。

SM2加密与解密算法原理的理解

        在理论学习阶段,我详细研究了SM2算法的工作机制,包括密钥对的生成、加密过程中的点乘运算、解密时私钥的应用等。通过编写代码实现这些算法,我不仅加深了对SM2加密算法的认识,还体会到了公钥密码体系中密钥管理的重要性。

感想:

        本次实验不仅让我对SM2加密算法有了更深刻的认识,也锻炼了我的编程能力和问题解决能力。在实际操作中遇到的难题,如参数选择的合理性、编码转换的准确性以及加密解密过程的可靠性,都促使我不断寻求解决方案,增强了我的实验技能和理论知识的应用能力。

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SM2椭圆曲线公钥密码算法是我国自主设计的公钥密码算法,用于实现数字签名、密钥交换和数据加密等功能。相比于RSA算法SM2算法基于椭圆曲线上点群离散对数难题,具有更高的密码强度。\[1\] 关于SM2算法的移植,目前已经在我国的密码应用领域得到广泛应用。国家密码管理局于2011年发布了《关于做好公钥密码算法升级工作的通知》,要求在建和拟建公钥密码基础设施电子认证系统和密钥管理系统中使用SM2算法。同时,自2011年7月1日起,投入运行并使用公钥密码的信息系统也应使用SM2算法。\[2\] SM2算法的移植工作主要包括将算法应用到相应的系统和设备中,并确保其安全性和性能。移植过程中需要考虑算法的实现细节、密钥管理、算法性能优化等方面的问题。同时,还需要进行相关的测试和验证,确保移植后的算法能够正常运行并满足安全需求。 总之,SM2椭圆曲线公钥密码算法已经在我国得到广泛应用,并且有相关的政策要求在密码应用系统中使用该算法。移植工作需要考虑算法的实现和性能优化,并进行相应的测试和验证。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* *3* [sm2国密算法的纯c语言版本,使用于单片机平台(静态内存分配)](https://blog.csdn.net/qq8864/article/details/100582268)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值