mirror of
				https://github.com/Mabbs/mabbs.github.io
				synced 2025-10-27 20:47:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			81 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			81 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| layout: post
 | ||
| title: Python国密算法使用探索
 | ||
| tags: [Python, GmSSL, 国密]
 | ||
| ---
 | ||
| 
 | ||
|   使用罕见的算法是什么感受😁<!--more-->    
 | ||
| 
 | ||
| # 起因
 | ||
|   前段时间因为某些原因需要对某些东西进行密评改造,需要使用国密算法。虽然国密算法也算进入标准了,但是网上能搜到的资料还是太少了(尤其是Python的,大多资料都是Java的),所以我打算自己研究研究。   
 | ||
|   
 | ||
| # 关于Python使用国密算法的方式
 | ||
|   其实在新版OpenSSL中已经支持了国密算法,比如SM3还有SM4,不过[pyOpenSSL](https://github.com/pyca/pyopenssl)似乎只有对非对称加密算法的支持……我倒是不在乎,因为在我实际应用里加解密都是服务器密码机处理的,我自己连密钥也看不到,所以不需要管怎么实现。但是签名验签还有摘要算法之类的理论上应该是可以自己实现的,毕竟算法是公开的。   
 | ||
|   关于摘要算法SM3我搜了一下,似乎因为它已经进入标准了,至少在新版的Python中可以用`hashlib.new("sm3")`这样的方式进行计算,但是旧版的Python用不了……所以如果要在旧版Python上处理还得自己想办法。   
 | ||
|   既然标准库不太能满足,那第三方库选哪个比较好呢?我看用的比较多的一个是封装C库[GmSSL](https://github.com/guanzhi/GmSSL)的[GmSSL-Python](https://github.com/GmSSL/GmSSL-Python),想要安装得先安装那个C库;还有一个是纯Python实现的[gmssl](https://github.com/duanhongyi/gmssl)。对我来说的话我更喜欢后面那个纯python实现的,虽然效率低了点,但是看起来比较简单(虽然看起来不是很专业🤣),那个C库包装的感觉有点复杂……而且这两个库有冲突,所以最终我选择了那个纯Python实现的版本。   
 | ||
| 
 | ||
| # 使用SM2withSM3进行验签
 | ||
|   在一些挑战应答方式的登录方式中就需要用到这种东西,服务器发送一个随机数让客户端用私钥签名,然后服务器用公钥进行验签。我看了一下那个库的“gmssl.sm2.CryptSM2”中有个verify_with_sm3方法挺符合需求的,但有个问题是它这个CryptSM2传入的公钥是串数字,但客户端传来的是证书……看来还得解析证书,我看pyOpenSSL库里有加载证书还有导出公钥的方法,但是那个导出的公钥也不是一串数字……后来看了半天,发现导出的公钥的倒数130位才是公钥😅……最终把所有的值带进去试了一下终于没问题了,最终的代码如下:   
 | ||
| ```python
 | ||
| import OpenSSL.crypto
 | ||
| from gmssl import sm2
 | ||
| import base64
 | ||
| 
 | ||
| certSign = ""   # 证书
 | ||
| signBytes = b"" # 签名
 | ||
| inData = b""    # 被签名的值
 | ||
| 
 | ||
| sm2.CryptSM2(
 | ||
|     private_key="",
 | ||
|     public_key=OpenSSL.crypto.dump_publickey(
 | ||
|         OpenSSL.crypto.FILETYPE_ASN1,
 | ||
|         OpenSSL.crypto.load_certificate(
 | ||
|             OpenSSL.crypto.FILETYPE_PEM,
 | ||
|             f"""-----BEGIN CERTIFICATE-----
 | ||
| {certSign}
 | ||
| -----END CERTIFICATE-----""".encode(),
 | ||
|         ).get_pubkey(),
 | ||
|     ).hex()[-128:],
 | ||
|     asn1=True,
 | ||
| ).verify_with_sm3(signBytes.hex(), inData)
 | ||
| ```
 | ||
| 
 | ||
| # 使用HMAC-SM3对数据进行消息验证
 | ||
|   这个其实新版的Python可以直接用,因为新版Python的hashlib里有SM3,所以一句   
 | ||
| ```python
 | ||
| hmac.new(key, data, digestmod="sm3").hexdigest()
 | ||
| ```
 | ||
|   就可以了,但是我用的是旧版的Python(macOS自带的3.9.6🤣)不支持……那怎么办呢?我看了一下这个函数的注释写的“digestmod”这个参数除了传hashlib支持的方法之外还可以传符合[PEP 247](https://peps.python.org/pep-0247/)的模块。显然无论是GmSSL-Python还是gmssl都没有符合这个规范。不过我可以自己写个适配器来适配这个规范。所以最终只好自己写一下了:   
 | ||
| ```python
 | ||
| import copy
 | ||
| import hmac
 | ||
| from gmssl import sm3
 | ||
| 
 | ||
| class sm3_adapter:
 | ||
|     def __init__(self):
 | ||
|         self.msg = []
 | ||
|         self.digest_size = 32
 | ||
|         self.block_size = 64
 | ||
| 
 | ||
|     def new(self):
 | ||
|         self.msg = []
 | ||
| 
 | ||
|     def update(self, data):
 | ||
|         self.msg += list(data)
 | ||
| 
 | ||
|     def copy(self):
 | ||
|         return copy.deepcopy(self)
 | ||
| 
 | ||
|     def digest(self):
 | ||
|         return bytes.fromhex(self.hexdigest())
 | ||
| 
 | ||
|     def hexdigest(self):
 | ||
|         return sm3.sm3_hash(self.msg)
 | ||
| 
 | ||
| key = b""   # 密钥
 | ||
| data = b""  # 数据
 | ||
| hmac.new(key, data, digestmod=sm3_adapter).hexdigest()
 | ||
| ```
 | ||
| 
 | ||
| # 感想
 | ||
|   这么看来使用国密算法加密倒是也没很复杂,但是和国际标准相比也没什么优势。虽然有些地方强制使用那确实没啥办法,但是想要普及肯定是不用想了,另外我自己的东西肯定是不敢用国密,虽然进了标准而且也开放了算法,但是很难说会不会像Dual_EC_DRBG算法那样偷偷插了后门 ~~(虽然我觉得他们应该没这个实力🤣)~~ ,但国际算法有后门我不怕,国内算法有后门那就吓人了🤣。 | 
