SM2椭圆曲线公钥加密/解密算法

SM2简介

SM2是非对称加密算法

它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名、密钥交换和公钥加密,用于替换RSA/DH/ECDSA/ECDH等国际算法。可以满足电子认证服务系统等应用需求,由国家密码管理局于2010年12月17号发布。

非对称密码算法 asymmetric cryptographic algorithm
使用公钥进行加密而使用私钥进行解密的一类密码算法,已知公钥求私钥在计算上不可行。

密钥 key
确定密码函数运算的一个参数,它用于:
a) 加密或解密变换;
b) 同步产生共享秘密;
c) 数字签名的生成或验证。

私钥 private key
在非对称密码体制中,实体自己保持的、只有实体自身知道的一种密钥。

公钥 public key
在非对称密码体制中,实体的一种可以公开的密钥。

秘密密钥 secret key
在密码体制中收发双方共同拥有的、而第三方不知道的一种密钥。

杂凑函数 hash function
将一个比特串映射为一个固定长度比特串的函数。该函数满足如下性质:
a) 对于任意给定的输出,要找到其对应的输入,在计算上是不可行的;
b) 对于任意给定的输入,要找到输出相同的另一个输入,在计算上是不可行的。
杂凑函数的输出为杂凑值 hash value
注:计算可行性依赖于具体的安全需求和环境。

密钥派生函数(KDF) key derivation function
通过作用于共享秘密和双方都知道的其它参数,产生一个或多个共享秘密密钥的函数。

作用是从一个共享的秘密比特串中派生出密钥数据。在密钥协商过程中,密钥派生函数作用在密钥交换所获共享的秘密比特串上,从中产生所需的会话密钥或进一步加密所需的密钥数据。
密钥派生函数需要调用密码杂凑函数。
设密码杂凑函数为Hv( ),其输出是长度恰为v比特的杂凑值。

密钥派生函数 K D F ( Z , k l e n ) KDF(Z, klen) KDF(Z,klen)
输入:比特串Z,整数klen(表示要获得的密钥数据的比特长度,要求该值小于 ( 232 − 1 ) v ) (232-1)v) (2321)v)
输出:长度为 k l e n klen klen的密钥数据比特串K。
a)初始化一个32比特构成的计数器 c t = 0 x 00000001 ct=0x00000001 ct=0x00000001
b)对i从1到 ⌈ k l e n / v ⌉ ⌈klen/v⌉ klen/v执行:
b.1)计算 H a i = H v ( Z ∣ ∣ c t ) Hai=Hv(Z || ct) Hai=Hv(Z∣∣ct)
b.2) c t + + ct++ ct++
c)若 k l e n / v klen/v klen/v是整数,令 H a ! ⌈ k l e n / v ⌉ = H a ⌈ k l e n / v ⌉ ,否则令 H a ! ⌈ k l e n / v ⌉ 为 H a ⌈ k l e n / v ⌉ 最左边的 ( k l e n − ( v × ⌊ k l e n / v ⌋ ) ) 比特 Ha!⌈klen/v⌉ = Ha⌈klen/v⌉,否则令Ha!⌈klen/v⌉为Ha⌈klen/v⌉最左边的(klen − (v × ⌊klen/v⌋))比特 Ha!klen/v=Haklen/v,否则令Ha!klen/vHaklen/v最左边的(klen(v×klen/v⌋))比特
d)令 K = H a 1 ∣ ∣ H a 2 ∣ ∣ ⋅ ⋅ ⋅ ∣ ∣ H a ⌈ k l e n / v ⌉ − 1 ∣ ∣ H a ! ⌈ k l e n / v ⌉ K = Ha1||Ha2|| · · · ||Ha⌈klen/v⌉−1||Ha!⌈klen/v⌉ K=Ha1∣∣Ha2∣∣⋅⋅⋅∣∣Haklen/v1∣∣Ha!klen/v

P b P_b Pb为用户B的公钥, d B d_B dB为用户B的私钥

加密算法

设需要发送的消息为比特串M,klen为M的比特长度。
为了对明文M进行加密,作为加密者的用户A应实现以下运算步骤:
A1:用随机数发生器产生随机数k∈[1,n-1];
A2:计算椭圆曲线点C1=[k]G=(x1,y1),将C1的数据类型转换为比特串;
A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
A4:计算椭圆曲线点[k]PB=(x2,y2),将坐标x2、y2 的数据类型转换为比特串;
A5:计算t=KDF(x2 || y2, klen),若t为全0比特串,则返回A1;
A6:计算C2 = M ⊕ t;
A7:计算C3 = Hash(x2 || M || y2);
A8:输出密文C = C1 || C2 || C3。

解密算法

设klen为密文中C2的比特长度。
为了对密文C=C1 || C2 || C3 进行解密,作为解密者的用户B应实现以下运算步骤:
B1:从C中取出比特串C1,将C1的数据类型转换为椭圆曲线上的点,验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
B2:计算椭圆曲线点S=[h]C1,若S是无穷远点,则报错并退出;
B3:计算[dB]C1=(x2,y2),将坐标x2、y2的数据类型转换为比特串;
B4:计算t=KDF(x2 || y2, klen),若t为全0比特串,则报错并退出;
B5:从C中取出比特串C2,计算M′ = C2 ⊕ t;
B6:计算u = Hash(x2 || M′ || y2),从C中取出比特串C3,若u ≠ C3,则报错并退出;
B7:输出明文M′。

具体细节参考
国家密码管理局关于发布《SM2椭圆曲线公钥密码算法》公告

加密和解密具体示例

椭圆曲线方程为: y 2 = x 3 + a x + b y^2 = x^3 + ax + b y2=x3+ax+b
示例1: F p − 256 F_p-256 Fp256

素数p:
8542D69E 4C044F18 E8B92435 BF6FF7DE 45728391 5C45517D 722EDB8B 08F1DFC3
系数a:
787968B4 FA32C3FD 2417842E 73BBFEFF 2F3C848B 6831D7E0 EC65228B 3937E498
系数b:
63E4C6D3 B23B0C84 9CF84241 484BFE48 F61D59A5 B16BA06E 6E12D1DA 27C5249A
基点G=(xG,yG),其阶记为n。
坐标xG:
421DEBD6 1B62EAB6 746434EB C3CC315E 32220B3B ADD50BDC 4C4E6C14 7FEDD43D
坐标yG:
0680512B CBB42C07 D47349D2 153B70C4 E5D7FDFC BFA36EA1 A85841B9 E46E09A2
阶n:
8542D69E 4C044F18 E8B92435 BF6FF7DD 29772063 0485628D 5AE74EE7 C32E79B7

待加密的消息M:encryption standard

消息M的16进制表示:656E63 72797074 696F6E20 7374616E 64617264

私钥dB:
1649AB77 A00637BD 5E2EFE28 3FBF3535 34AA7F7C B89463F2 08DDBC29 20BB0DA0
公钥PB=(xB,yB):
坐标xB:
435B39CC A8F3B508 C1488AFC 67BE491A 0F7BA07E 581A0E48 49A5CF70 628A7E0A
坐标yB:
75DDBA78 F15FEECB 4C7895E2 C1CDF5FE 01DEBB2C DBADF453 99CCF77B BA076A42

加密各步骤中的有关值:

产生随机数k:
4C62EEFD 6ECFC2B9 5B92FD6C 3D957514 8AFA1742 5546D490 18E5388D 49DD7B4F
计算椭圆曲线点C1=[k]G=(x1,y1):
坐标x1:
245C26FB 68B1DDDD B12C4B6B F9F2B6D5 FE60A383 B0D18D1C 4144ABF1 7F6252E7
坐标y1:
76CB9264 C2A7E88E 52B19903 FDC47378 F605E368 11F5C074 23A24B84 400F01B8

在此C1选用未压缩的表示形式,点转换成字节串的形式为 P C ∥ x 1 ∥ y 1 P C∥x_1∥y_1 PCx1y1,其中 P C P C PC为单一字节
且 P C = 04 且P C=04 PC=04,仍记为C1。
计算椭圆曲线点 [ k ] P B = ( x 2 , y 2 ) : [k]PB=(x2,y2): [k]PB=(x2,y2)

坐标x2:
64D20D27 D0632957 F8028C1E 024F6B02 EDF23102 A566C932 AE8BD613 A8E865FE
坐标y2:
58D225EC A784AE30 0A81A2D4 8281A828 E1CEDF11 C4219099 84026537 5077BF78
消息M的比特长度klen=152
计算t=KDF(x2||y2, klen):
006E30 DAE231B0 71DFAD8A A379E902 64491603
计算C2=M⊕t:
650053 A89B41C4 18B0C3AA D00D886C 00286467
计算C3=Hash(x2 || M || y2): x2 || M || y2:
64D20D27 D0632957 F8028C1E 024F6B02 EDF23102 A566C932 AE8BD613 A8E865FE
656E6372 79707469 6F6E2073 74616E64 61726458 D225ECA7 84AE300A 81A2D482
81A828E1 CEDF11C4 21909984 02653750 77BF78
C3:
9C3D7360 C30156FA B7C80A02 76712DA9 D8094A63 4B766D3A 285E0748 0653426D

输出密文 C = C 1 ∥ C 2 ∥ C 3 : C = C1∥C2∥C3: C=C1∥C2∥C3

04245C26 FB68B1DD DDB12C4B 6BF9F2B6 D5FE60A3 83B0D18D 1C4144AB F17F6252
E776CB92 64C2A7E8 8E52B199 03FDC473 78F605E3 6811F5C0 7423A24B 84400F01
B8650053 A89B41C4 18B0C3AA D00D886C 00286467 9C3D7360 C30156FA B7C80A02
76712DA9 D8094A63 4B766D3A 285E0748 0653426D

解密各步骤中的有关值:
计算椭圆曲线点 [ d B ] C 1 = ( x 2 , y 2 ) : [dB]C1=(x2, y2): [dB]C1=(x2,y2)

坐标x2:
64D20D27 D0632957 F8028C1E 024F6B02 EDF23102 A566C932 AE8BD613 A8E865FE
坐标y2:
58D225EC A784AE30 0A81A2D4 8281A828 E1CEDF11 C4219099 84026537 5077BF78
计算t = KDF(x2||y2,klen):
006E30 DAE231B0 71DFAD8A A379E902 64491603
计算M′ = C2 ⊕ t:
656E63 72797074 696F6E20 7374616E 64617264
计算u =Hash(x2||M′||y2):
9C3D7360 C30156FA B7C80A02 76712DA9 D8094A63 4B766D3A 285E0748 0653426D

明文M′:656E63 72797074 696F6E20 7374616E 64617264,即为:encryption standard

SM2密钥生成以及加密解密具体代码示例

from gmssl import sm2
import random
import math

def SM2_enc(plaintext, pk):
    ciphertext = sm2_crypt.encrypt(plaintext)
    return ciphertext


# SM2 decryption
def SM2_dec(ciphertext, sk):
    plaintext = sm2_crypt.decrypt(ciphertext)
    return plaintext


# SM2 experiment with string

# 密钥生成
# key generation

def SM2_Mulyipoint(k, P, a, p):  # 多倍点运算
    k_b = bin(k).replace('0b', '')  # 按2^i分层逐层运算
    i = len(k_b) - 1
    R = P
    if i > 0:
        k = k - 2 ** i
        while i > 0:
            R = SM2_Pluspoint(R, R, a, p)
            i -= 1
        if k > 0:
            R = SM2_Pluspoint(R, SM2_Mulyipoint(k, P, a, p), a, p)
    return R


def SM2_Pluspoint(P, Q, a, p):  # 双倍点运算
    if (math.isinf(P[0]) or math.isinf(P[1])) and (~math.isinf(Q[0]) and ~math.isinf(Q[1])):  # OP = P
        R = Q
    elif (~math.isinf(P[0]) and ~math.isinf(P[1])) and (math.isinf(Q[0]) or math.isinf(Q[1])):  # PO = P
        R = P
    elif (math.isinf(P[0]) or math.isinf(P[1])) and (math.isinf(Q[0]) or math.isinf(Q[1])):  # OO = O
        R = [float('inf'), float('inf')]
    else:
        if P != Q:
            l = SM2__Mod_Decimal(Q[1] - P[1], Q[0] - P[0], p)
        else:
            l = SM2__Mod_Decimal(3 * P[0] ** 2 + a, 2 * P[1], p)
        x = SM2_Mod(l ** 2 - P[0] - Q[0], p)
        y = SM2_Mod(l * (P[0] - x) - P[1], p)
        R = [x, y]
    return R


def SM2_Mod(a, b):  # 摸运算
    if math.isinf(a):
        return float('inf')
    else:
        return a % b


def SM2__Mod_Decimal(n, d, b):  # 小数的模运算
    if d == 0:
        x = float('inf')
    elif n == 0:
        x = 0
    else:
        a = bin(b - 2).replace('0b', '')
        y = 1
        i = 0
        while i < len(a):  # n/d = x mod b => x = n*d^(b-2) mod b
            y = (y ** 2) % b  # 快速指数运算
            if a[i] == '1':
                y = (y * d) % b
            i += 1
        x = (y * n) % b
    return x


def key_gen(a, p, n, G):  # SM2密钥对的生成

    sk = random.randint(1, n - 2)
    pk = SM2_Mulyipoint(sk, G, a, p)
    return sk, pk


def write_key():
    p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
    a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
    b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
    n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
    Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
    Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
    G = [Gx, Gy]
    [sk, pk] = key_gen(a, p, n, G)
    fw = open("D:/d_B.txt", 'w')
    fw.write("%X" % sk)

    fw = open("D:/P_B.txt", 'w')
    fw.write("%X%X" % (pk[0], pk[1]))


def exp_SM2_str(plaintext):
    plaintext_bytes = bytes(plaintext, encoding="utf8")
    return plaintext_bytes


if __name__ == '__main__':
    write_key()
    sf = open("D:\d_B.txt")
    sk = (sf.read())
    print("sk为:", sk)
    f = open("D:\P_B.txt")
    pk = (f.read())
    print("pk为:", pk)

    sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
    print("请输入需要加密的内容:")
    plaintext = (input())
    plaintext_bytes = exp_SM2_str(plaintext)
    ciphertext = SM2_enc(plaintext_bytes, pk)
    print("密文为:", ciphertext)
    plaintext = SM2_dec(ciphertext, sk)
    print("明文为:", plaintext)

SM2椭圆曲线公钥密码算法推荐曲线参数

推荐使用素数域256位椭圆曲线。
椭圆曲线方程: y 2 = x 3 + a x + b y^2 = x^3 + ax + b y2=x3+ax+b
曲线参数:
p=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
a=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
b=28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
n=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
Gy =BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0

参考

https://sca.gov.cn/sca/xxgk/2010-12/17/content_1002386.shtml
Python 在gmssl 下完成国密算法 SM2 十六进制公钥密钥随机生成

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值