国密sm2与sm4加密解密教程

国密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))

[参考]https://github.com/guanzhi/GmSSL

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容