SM3 是国产的一种密码散列函数标准,由国家密码管理局于2010年12月17日发布。相关标准为 GM/T 0004-2012《SM3密码杂凑算法》。类似于我们平常经常接触到的 MD5算法,我们可以认为是 MD5 的国产对应版本。
在一些政务服务系统中,有要求必须使用国产 SM3 进行加解密处理。SM3主要用于数字签名及验证、消息认证码生成及验证、随机数生成等,其算法是公开的。政务云加密的格式示例如下:
print(b64encode(enc_key).decode('utf-8'))
rbPb15S/Z9t+agffno5wuhB77VbRi6F9Iv2qIxU7WHw=
print(b64encode(mac_key).decode('utf-8'))
G9GtHFE1YluXY1zWPlYk1e/nWfu0WSEb0KRcjhDeP/o=
但在使用 HmacSM3加密的开发过程中,发现网上虽然有不少 hmacSM3 加密的资料,但我没有找到使用 key 结合 sm3进行加密的。很多都是直接将数据进行 sm3加密,并不能满足我这里调用政务云加密的需要。也因此导致签名 signature一直不成功。
最终经过多次测试、试验找到了好办法。Python实现 国密 HmacSM3 加密的方法有多种,但也有一些区别。我这里测试通过了3种,总结出此篇文章。
一,使用专门的 gmssl 库。
先使用 pip install gmssl 安装gmssl python 库,导入后使用其中的 sm3 加密,需要注意的是,国密sm3加密要加密的数据内容要求是 bytes 类型数据。加密后返回 哈希值。
from gmssl import sm3
def fun_sm3_hash(data: str):
data_list = [i for i in bytes(data.encode('UTF-8'))]
hash_val = sm3.sm3_hash(data_list)
return hash_val
data = "hello, linge"
print(fun_sm3_hash(data))
需要注意,上面的方法未使用到 key, 另外通过以上的方法加密出来的数据是 64 位长的 16进制数据。输出结果为:
a050daf6a9bb9153a32b1f880afbf36aaf88a1fe2ac6437fd5a801793862a610
二,使用通用的 hashlib 库。
在Python中,常用的第三方库 hashlib 也可以用来实现 SM3 加密。hashlib是Python标准库中的一个模块,提供了常见的哈希算法,其中包括SM3,这就给我们带来了很多的便利,不用专门去安装 gmssl 模块,如果是docker 镜像就不用更新镜像了。代码如下:
# 创建SM3对象,传入数据后已计算 hash 很像 md5的用法。
import hashlib
sm3 = hashlib.new('sm3')
data = "hello, linge"
sm3.update(data.encode('utf-8'))
hash_value = sm3.hexdigest()
print(hash_value)
需要注意,上面的方法也未使用到 key, 另外通过以上的方法加密出来的数据是 64 位长的 16进制数据。输出结果同上也为:
a050daf6a9bb9153a32b1f880afbf36aaf88a1fe2ac6437fd5a801793862a610
上面的两种方法我都未找到加入 key 的方法,大家可以到比如 hashlib 官网中去看看:https://docs.python.org/zh-cn/3.13/library/hashlib.html 也许有这样的方法,但我没有找到。如果要加入 key,我这里经过描
三、使用原生的密钥哈希模式 hmac
import hmac
key = b"secret"
data = b"Hello, Linge!"
hmac_value = hmac.new(key, data, digestmod="sm3").hexdigest()
print(hmac_value)
通过这种方法可以引入key,最后的输出结果为:
17aeae7eb061fe6950df13cf77bd9c22700bd15321b5dfb1710a71d41d5a3ba8
在一些政务云服务中基本要求这种用法,但在网上我没有看到有这类内容的文章。政务云的处理一般要要进行base64处理。如下过程,最后处理成这样的数据:
import hmac,base64
key = b"secret"
data = b"Hello, Linge!"
#用digest() 输出二进制格式数据 不用 hexdigest()输出16进制数据
hmac_value = hmac.new(key, data, digestmod="sm3").digest()
sign = base64.b64encode(hmac_value)
return sign.decode('utf-8')