国密sm2与sm4加密解密教程
一、加密过程
安装
pip install
1.1 导包
import base64
from gmssl import sm2,func
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
- 其中func用来生成随机数,
- CryptSM4用来创建sm4对象,
- SM4_ENCRYPT加密时候sm4的参数,
- SM4_DECRYPT解密时候sm4的参数
1.2 公私钥生成
国密的公私钥格式有规定,gmssl(python)没有生成工具,gmssl(c)、java跟openSSL,以及支付宝开放平台助手生成的公私钥带入后虽然可以加密解密,但是解密结果是错误的.故自己写一个公私钥生成代码,sm2utils.py.文件内容放最后.如下为生成的公私钥:
private_key= "228a9707053e1b333fb8cb839567a9db4ca1cf5381e9a6a539774e6c3563cdfa"
public_key="893cb9392dabd2fac095f657a7e0bc308e32f4b79380d478547f57123dccb3bc4a3a2d009f5826b6624c99dd41baf470a8baf46722f2d36b1d26f19af112c5cd"
1.3 创建sm4Key和sm4对象
sm4Key_str = func.random_hex(16) # 随机产生16位 'a2bc65bca8e41795'
sm4Key = bytes(sm4Key_str,encoding='utf-8') # 字符串转bytes
sm4_crypt =CryptSM4() # 初始化CryptSM4
sm4_crypt.set_key(sm4Key,mode=SM4_ENCRYPT) # 初始化key到CryptSM4 这里传的是SM_DECRYPT
1.4 sm4对象对数据进行加密
data = [1,2,3,4,5,6,6,7]
data= str(data).encode("utf-8") # 列表转字符串转bytes
encryptData = sm4_crypt.crypt_ecb(data) #对数据(bytes)加密 两种方式crypt_ecb跟crypt_cbc
encryptData= base64.b64encode(encryptData) #bytes 转base64
encryptData = encryptData.decode("utf-8") # 由于转为base64,还是bytes,json不支持,故转为str
1.5 创建sm2对象(附公钥),对sm4Key进行加密
sm2_crypt =sm2.CryptSM2(private_key=None,public_key=public_key) # 附公钥
encryptKey = sm2_crypt.encrypt(sm4Key) # 对sm4Key(bytes) 进行加密,返回bytes
encryptKey =base64.b64encode(encryptKey) #bytes 转base64
encryptKey = encryptKey.decode("utf-8") # 由于转为base64,还是bytes,json不支持,故转为str
1.6 sm2(附私钥)对sm4Key签名
sm2_crypt_s =sm2.CryptSM2(private_key=private_key,public_key=None) #私钥签名, 公钥验证
random_hex_str = func.random_hex(sm2_crypt.para_len) # para_len为64, 生成64位随机字符串
sign = sm2_crypt_s.sign(sm4Key,random_hex_str) # 签名需要用私钥 , sign为字符串格式
sign = base64.b64encode(bytes(sign,encoding='utf-8')) # 字符串转base64
sign = sign.decode("utf-8") # bytes转字符串
1.7 返回结果
result = {"sign":sign,"encryptKey":encryptKey,"encryptData":encryptData}
二、解密
sign = base64.b64decode(result['sign'])
encryptKey =base64.b64decode(result["encryptKey"])
encryptData = base64.b64decode(result["encryptData"])
2.1 sm2(附私钥)解密sm4Key
sm2_crypt_r =sm2.CryptSM2(private_key=private_key,public_key=None)
sm4Key_r = sm2_crypt_r.decrypt(encryptKey) # 解密, 返回bytes
# assert sm4Key_r==sm4Key # 开发者验证, 工程中无法使用
2.2 公钥签名验证
sm2_crypt_sr =sm2.CryptSM2(private_key=None,public_key=public_key)
assert sm2_crypt_sr.verify(sign,sm4Key_r)
验证对了,说明j解密出来的钥匙sm4Key_r正确,即提供encrytKey正确,可以进行下一步解密数据,否则会报错
2.3 sm4解密数据
sm4_crypt_r=CryptSM4()
sm4_crypt_r.set_key(sm4Key_r,mode=SM4_DECRYPT) # todo 这里传的是SM_DECRYPT
data_r=sm4_crypt_r.crypt_ecb(encryptData)
# assert data_r == data # 实际应用无法用此来验证,只能用签名验证
data_r = eval(data_r.decode('utf-8')) # 转为列表
三、sm2uils.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
)
a=SystemRandom().randrange(1, sm2p256v1.N)
print(".N=",sm2p256v1.N,"\na=",a)
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))