SM2算法
简述
SM2是非对称加密算法
它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名、密钥交换和公钥加密,用于替换RSA/DH/ECDSA/ECDH等国际算法。可以满足电子认证服务系统等应用需求,由国家密码管理局于2010年12月17号发布。
SM2采用的是ECC 256位的一种,其安全强度比RSA 2048位高,且运算速度快于RSA。随着密码技术和计算技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。SM2算法在安全性、性能上都具有优势。
基础知识:椭圆曲线知识点、SM3算法
获取公私钥:
椭圆曲线方程: y 2 = x 3 + a x + b m o d p y^2=x^3+ax+b \mod p y2=x3+ax+bmodp
- 确 认 a 、 b 、 p , 确 认 曲 线 。 确认a、b、p,确认曲线。 确认a、b、p,确认曲线。
- 选 择 一 个 点 P ( x g , y g ) 为 基 点 。 选择一个点P(x_g,y_g)为基点。 选择一个点P(xg,yg)为基点。
- 对 曲 线 做 切 线 、 x 对 称 点 运 行 。 次 数 为 d , 运 算 倍 点 为 Q 对曲线做切线、x对称点运行。次数为 d,运算倍点为Q 对曲线做切线、x对称点运行。次数为d,运算倍点为Q
- d 为 私 钥 , Q 为 公 钥 d 为私钥,Q为公钥 d为私钥,Q为公钥
密钥对的生成:
- 产 生 随 机 整 数 d [ 1 , n − 2 ] 产生随机整数 d [1,n-2] 产生随机整数d[1,n−2]
- G 为 基 点 , 计 算 点 P = ( x P , y P ) = [ d ] G ; G为基点,计算点 P=(xP,yP)=[d]G; G为基点,计算点P=(xP,yP)=[d]G;
- 密 钥 对 为 : ( d , P ) 其 中 , d 为 私 钥 , P 为 公 钥 密钥对为: (d,P) 其中,d为私钥,P为公钥 密钥对为:(d,P)其中,d为私钥,P为公钥
一个很典型的例子:
a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
x_g = 0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7
y_g = 0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
SM签名
M M M为待签名消息,数字签名结果为 ( r , s ) (r,s) (r,s) ,用户密钥对 ( d , P ) (d,P) (d,P)。
实现步骤:
- e = h a s h ( M ) ⇒ 获 取 消 息 散 列 值 e=hash(M) \qquad \Rightarrow获取消息散列值 e=hash(M)⇒获取消息散列值
- 产 生 随 机 数 k ⇒ 以 便 即 使 是 同 一 个 消 息 , 每 次 签 名 出 来 的 结 果 不 同 。 产生随机数 k \qquad \Rightarrow以便即使是同一个消息,每次签名出来的结果不同。 产生随机数k⇒以便即使是同一个消息,每次签名出来的结果不同。
- 使 用 随 机 数 , 计 算 椭 圆 曲 线 点 ( x 1 , y 1 ) = [ k ] G 使用随机数,计算椭圆曲线点(x_1,y_1)=[k]G 使用随机数,计算椭圆曲线点(x1,y1)=[k]G
- r = ( e + x 1 ) m o d n ⇒ 判 断 : r = 0 或 者 r + k = n , 继 续 第 2 步 。 r=(e+x1) \mod n \qquad \Rightarrow判断:r=0 或者 r+k=n, 继续第2步。 r=(e+x1)modn⇒判断:r=0或者r+k=n,继续第2步。
- s = ( ( 1 + d ) − 1 ∗ ( k − r ∗ d ) ) m o d n , 若 s = 0 , 继 续 第 2 步 s= ((1+d)^{-1} * (k-r*d)) \mod n , 若s=0,继续第2步 s=((1+d)−1∗(k−r∗d))modn,若s=0,继续第2步
- r , s 为 签 名 信 息 。 r,s 为签名信息。 r,s为签名信息。
SM验签
M M M为明文, ( r , s ) (r,s) (r,s)为签名结果,用户公钥 P P P
实现步骤:
- e = h a s h ( M ) e=hash(M) e=hash(M)
- t = ( r + s ) m o d n t=(r+s) \mod n t=(r+s)modn
- ( x , y ) = [ s ] G + [ t ] P (x,y)=[s]G + [t]P (x,y)=[s]G+[t]P
- R = ( e + x ) m o d n R=(e+x)\mod n R=(e+x)modn
- 计 算 R 是 否 等 于 r 计算R是否等于r 计算R是否等于r
[ s ] G [s]G [s]G + [ t ] P [t]P [t]P的结果可以推导出等于 [ k ] G [k]G [k]G。
验证原理
[
s
]
G
+
[
t
]
P
=
s
G
+
(
r
+
s
)
P
[s]G+[t]P=sG+(r+s)P
[s]G+[t]P=sG+(r+s)P
=
s
G
+
(
r
+
s
)
d
G
\qquad \qquad \quad=sG+(r+s)dG
=sG+(r+s)dG
=
s
G
+
s
d
G
+
r
d
G
\qquad \qquad \quad=sG+sdG+rdG
=sG+sdG+rdG
=
(
1
+
d
)
s
G
+
r
d
G
\qquad \qquad \quad=(1+d)sG+rdG
=(1+d)sG+rdG
=
(
1
+
d
)
(
1
+
d
)
−
1
(
k
−
r
d
)
G
+
r
d
G
\qquad \qquad \quad=(1+d)(1+d)^{−1}(k−rd)G+rdG
=(1+d)(1+d)−1(k−rd)G+rdG
=
(
k
−
r
d
)
G
+
r
d
G
\qquad \qquad \quad=(k−rd)G+rdG
=(k−rd)G+rdG
=
k
G
−
r
d
G
+
r
d
G
\qquad \qquad \quad=kG−rdG+rdG
=kG−rdG+rdG
=
k
G
=
(
x
1
,
y
1
)
\qquad \qquad \quad=kG=(x1,y1)
=kG=(x1,y1)
SM加密
M为明文字符串
- 获 取 随 机 数 k 获取随机数 k 获取随机数k
- ( x 1 , y 1 ) = [ k ] G (x1, y1) = [k]G (x1,y1)=[k]G
- S = [ h ] P ⇒ h 为 余 因 子 S=[h]P \qquad \Rightarrow h为余因子 S=[h]P⇒h为余因子
- C 1 = ( x 2 , y 2 ) = [ k ] P C1=(x2,y2)= [k]P C1=(x2,y2)=[k]P
- t = K D F ( x 2 ∥ y 2 , k l e n ) ⇒ k l e n 为 M 的 长 度 。 K D F 是 s m 2 的 密 钥 派 生 函 数 t=KDF(x2\parallel y2,klen) \qquad \Rightarrow klen为M的长度。KDF是sm2的密钥派生函数 t=KDF(x2∥y2,klen)⇒klen为M的长度。KDF是sm2的密钥派生函数
- C 2 = M + t C2 = M+t C2=M+t
- C 3 = H a s h ( x 2 ∥ M ∥ y 2 ) C3 = Hash(x2\parallel M\parallel y2) C3=Hash(x2∥M∥y2)
- C = C 1 ∥ C 2 ∥ C 3 C = C1\parallel C2\parallel C3 C=C1∥C2∥C3
SM解密
C为密文字符串,klen为密文中C2的长度
- C 1 = C 里 面 获 取 , 验 证 C 1 是 否 满 足 椭 圆 曲 线 。 ⇒ C 2 长 度 确 定 , 可 以 获 取 C 1 内 容 。 C1 = C里面获取 ,验证C1是否满足椭圆曲线。 \qquad \Rightarrow C2长度确定,可以获取C1内容。 C1=C里面获取,验证C1是否满足椭圆曲线。⇒C2长度确定,可以获取C1内容。
- S = [ h ] C 1 , S 为 无 穷 点 , 退 出 。 S=[h]C1,S为无穷点,退出。 S=[h]C1,S为无穷点,退出。
- ( x 2 , y 2 ) = [ d ] C 1 (x2,y2)=[d]C1 (x2,y2)=[d]C1
- t = K D F ( m 2 ∥ y 2 , k l e n ) t=KDF(m2\parallel y2,klen) t=KDF(m2∥y2,klen)
- M ~ = C 2 + t \widetilde{M} = C2+t M =C2+t
- u = H a s h ( x 2 ∥ M ~ ∥ y 2 ) , u ? = = C 3 u=Hash(x2\parallel \widetilde{M} \parallel y2), u ?== C3 u=Hash(x2∥M ∥y2),u?==C3
- M ~ 为 明 文 \widetilde{M}为明文 M 为明文
代码实现
首先,需要导入包gmssl
pip install gmssl
生成公私钥算法
sm2utils.py
from random import SystemRandom
class CurveFp:
def __init__(self, A, B, P, N, Gx, Gy, name):
self.A = A
self.B = B
self.P = P
self.N = N
self.Gx = Gx
self.Gy = Gy
self.name = name
sm2p256v1 = CurveFp(
name="sm2p256v1",
A=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC,
B=0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93,
P=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF,
N=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123,
Gx=0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7,
Gy=0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
)
def multiply(a, n, N, A, P):
return fromJacobian(jacobianMultiply(toJacobian(a), n, N, A, P), P)
def add(a, b, A, P):
return fromJacobian(jacobianAdd(toJacobian(a), toJacobian(b), A, P), P)
def inv(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high//low
nm, new = hm-lm*r, high-low*r
lm, low, hm, high = nm, new, lm, low
return lm % n
def toJacobian(Xp_Yp):
Xp, Yp = Xp_Yp
return (Xp, Yp, 1)
def fromJacobian(Xp_Yp_Zp, P):
Xp, Yp, Zp = Xp_Yp_Zp
z = inv(Zp, P)
return ((Xp * z**2) % P, (Yp * z**3) % P)
def jacobianDouble(Xp_Yp_Zp, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if not Yp:
return (0, 0, 0)
ysq = (Yp ** 2) % P
S = (4 * Xp * ysq) % P
M = (3 * Xp ** 2 + A * Zp ** 4) % P
nx = (M**2 - 2 * S) % P
ny = (M * (S - nx) - 8 * ysq ** 2) % P
nz = (2 * Yp * Zp) % P
return (nx, ny, nz)
def jacobianAdd(Xp_Yp_Zp, Xq_Yq_Zq, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
Xq, Yq, Zq = Xq_Yq_Zq
if not Yp:
return (Xq, Yq, Zq)
if not Yq:
return (Xp, Yp, Zp)
U1 = (Xp * Zq ** 2) % P
U2 = (Xq * Zp ** 2) % P
S1 = (Yp * Zq ** 3) % P
S2 = (Yq * Zp ** 3) % P
if U1 == U2:
if S1 != S2:
return (0, 0, 1)
return jacobianDouble((Xp, Yp, Zp), A, P)
H = U2 - U1
R = S2 - S1
H2 = (H * H) % P
H3 = (H * H2) % P
U1H2 = (U1 * H2) % P
nx = (R ** 2 - H3 - 2 * U1H2) % P
ny = (R * (U1H2 - nx) - S1 * H3) % P
nz = (H * Zp * Zq) % P
return (nx, ny, nz)
def jacobianMultiply(Xp_Yp_Zp, n, N, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if Yp == 0 or n == 0:
return (0, 0, 1)
if n == 1:
return (Xp, Yp, Zp)
if n < 0 or n >= N:
return jacobianMultiply((Xp, Yp, Zp), n % N, N, A, P)
if (n % 2) == 0:
return jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P)
if (n % 2) == 1:
return jacobianAdd(jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P), (Xp, Yp, Zp), A, P)
class PrivateKey:
def __init__(self, curve=sm2p256v1, secret=None):
self.curve = curve
self.secret = secret or SystemRandom().randrange(1, curve.N)
def publicKey(self):
curve = self.curve
xPublicKey, yPublicKey = multiply((curve.Gx, curve.Gy), self.secret, A=curve.A, P=curve.P, N=curve.N)
return PublicKey(xPublicKey, yPublicKey, curve)
def toString(self):
return "{}".format(str(hex(self.secret))[2:].zfill(64))
class PublicKey:
def __init__(self, x, y, curve):
self.x = x
self.y = y
self.curve = curve
def toString(self, compressed=True):
return {
True: str(hex(self.x))[2:],
False: "{}{}".format(str(hex(self.x))[2:].zfill(64), str(hex(self.y))[2:].zfill(64))
}.get(compressed)
if __name__ == "__main__":
priKey = PrivateKey()
pubKey = priKey.publicKey()
print(priKey.toString())
print(pubKey.toString(compressed = False))
加解密算法
封装类 sm2encryp.py
from gmssl import sm2
from base64 import b64encode, b64decode
# sm2的公私钥
SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
SM2_PUBLIC_KEY = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=SM2_PUBLIC_KEY, private_key=SM2_PRIVATE_KEY)
class sm2Encrypt:
# 加密
def encrypt(self, info):
encode_info = sm2_crypt.encrypt(info.encode(encoding="utf-8"))
encode_info = b64encode(encode_info).decode() # 将二进制bytes通过base64编码
return encode_info
# 解密
def decrypt(self, info):
decode_info = b64decode(info.encode()) # 通过base64解码成二进制bytes
decode_info = sm2_crypt.decrypt(decode_info).decode(encoding="utf-8")
return decode_info
if __name__ == "__main__":
origin_pwd = '123456'
sm2 = sm2Encrypt()
# 加密的密码
encrypy_pwd = sm2.encrypt(origin_pwd)
print(encrypy_pwd)
# 解密的密码
decrypt_pwd = sm2.decrypt(encrypy_pwd)
print(decrypt_pwd)
当跟sm2encryp.py在一个文件夹是可以直接引用它
from sm2encryp import sm2Encrypt
pass_encrypt = sm2Encrypt()
pwd = pass_encrypt.decrypt("H24OlVZgSTtevCW138O+C5PlZp8OiD920JnpVr7r9ndkGBWFZUVDD48iIVrZRnamgosV5910m9k0438WpIyi0guEt8F5inG7Y5A51whRfdPZ+qdvWVQxI857CBEzkb3h1bMp1ETQ")
print(pwd)
参考
https://www.jianshu.com/p/efc43060e0aa
https://blog.csdn.net/u013137970/article/details/84573200
https://blog.csdn.net/u014651560/article/details/113744296
更多推荐
所有评论(0)