由于工作原因,需要使用到国密加密算法,而且需要使用Python来做,于是就找来了Python中的gmssl库函数来进行使用。
在使用gmssl库的时候,可以使用在这个函数当中进行上面sm2自我签名与验签,但是其他工具产生的sm2签名无法通过该工具进行签名验证。在查找了GitHub之后,发现了问题所在,在对于sm2签名的时候,该函数是直接对于源数据进行签名,在算法的标准当中,签名是需要进行hash算法,对于源数据进行hash计算,产生中间值e,签名是对e进行签名的,同样,验签也是对e进行验签,所以该函数是不正确的。
改进办法,采用函数,对于原始数据进行hash计算,得出e值之后,使用e值进行签名与验签,即可和其他工具进行通用。
首先对于用户输入的参数进行计算Za值,使用sm3算法,算法的过程如下:
def sm2_hash_e(data, id, idlen, publickey):
a = b'\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' \
b'\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC'
b = b'\x28\xE9\xFA\x9E\x9D\x9F\x5E\x34\x4D\x5A\x9E\x4B\xCF\x65\x09\xA7' \
b'\xF3\x97\x89\xF5\x15\xAB\x8F\x92\xDD\xBC\xBD\x41\x4D\x94\x0E\x93'
gx = b'\x32\xc4\xae\x2c\x1f\x19\x81\x19\x5f\x99\x04\x46\x6a\x39\xc9\x94' \
b'\x8f\xe3\x0b\xbf\xf2\x66\x0b\xe1\x71\x5a\x45\x89\x33\x4c\x74\xc7'
gy = b'\xbc\x37\x36\xa2\xf4\xf6\x77\x9c\x59\xbd\xce\xe3\x6b\x69\x21\x53' \
b'\xd0\xa9\x87\x7c\xc6\x2a\x47\x40\x02\xdf\x32\xe5\x21\x39\xf0\xa0'
pubkey_xtemp = publickey[0:64]
pubkey_ytemp = publickey[64:128]
pubkey_x = str2bytes(pubkey_xtemp, 64)
pubkey_y = str2bytes(pubkey_ytemp, 64)
if idlen == 0:
entl = b'\x00\x00'
Zatemp = entl + a + b + gx + gy + pubkey_x + pubkey_y
else:
lentemp = 4 * idlen
entl = int.to_bytes(lentemp, 2, byteorder='big', signed=False)
idtemp = str2bytes(id, idlen)
Zatemp = entl + idtemp + a + b + gx + gy + pubkey_x + pubkey_y
Zatemp_v2 = byte2hex(Zatemp)
在此之后对于Za值与原始数据进行结合,计算出可以使用的e值
etemp = Za + data
e = Hash_sm3(etemp, 1)
resbytes = str2bytes(e, 64)
此时的e值可以作为Python中gmssl库中的验签函数的原始数据输入,可以和别的工具相互验证
对于验证的sm2值,需要考虑到的还有是否需要使用userID,在使用userID的时候,对于za值的计算是有一些区别的,在处理的时候,封装成为一个接口函数,此时由于使用的原因,我接收的参数都是十六进制的bytes值,所以我封装成了对于bytes验证的函数:
def SM2_sign_verify(originaldata, publickey, signvalue, id=''):
pubkey = publickey.hex()
origin = originaldata.hex()
sign = signvalue.hex()
sm2_crypt = sm2.CryptSM2(
public_key=pubkey, private_key='')
idlen = len(id)
if idlen == 0:
e = sm2_hash_e(origin, 0, 0, pubkey)
else:
e = sm2_hash_e(origin, id, idlen, pubkey)
try:
assert sm2_crypt.verify(sign, e)
return 1
except:
return 0
这一部分的代码可以根据具体的需求进行更改,参数一是原始数据,参数二是公钥,参数三是签名值,参数四是userID(可以为空),有需求的话可以根据情况在函数开始处转换数据格式。
该函数验证成功返回1,失败则返回0。
具体实现部分打包成文件上传,配合上面的函数接口与gmssl库进行使用,即可实现与别的签名工具互通签名与签名验证。