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)
(232−1)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⌉=Ha⌈klen/v⌉,否则令Ha!⌈klen/v⌉为Ha⌈klen/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∣∣⋅⋅⋅∣∣Ha⌈klen/v⌉−1∣∣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
Fp−256
素数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
PC∥x1∥y1,其中
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 十六进制公钥密钥随机生成