Python国密算法实战:cryptography与gmssl库兼容陷阱与完整解决方案

实践教程 · 2026-06-28 · 6 阅读

前言:Python 国密生态的"分裂"现状

2026 年,Python 国密开发者的处境仍然尴尬:没有一个库能覆盖所有国密算法,也没有两个库能无缝协作。

  • cryptography(v48.x)支持 SM3、SM4,但不支持 SM2
  • gmssl(v3.2.x)支持 SM2、SM3、SM4,但 SM4 在 CBC 解密时有 bug
  • 两者密钥格式不混用,同一份数据在两库间需要格式转换
这种"分裂"导致开发者经常在论坛里问:"为什么我拿 cryptography 生成的 SM4 密文,gmssl 解密出来是乱码?"

本文的目标很简单:给你一份能在生产环境直接运行的代码,彻底解决库间兼容问题。

环境准备

BASH
# Python 3.10+
pip install cryptography>=44.0 gmssl>=3.0

# 验证安装
python3 -c "
import cryptography, gmssl
print(f'cryptography {cryptography.__version__}')  # 48.0.0
print(f'gmssl {gmssl.__version__}')  # 3.2.x
"

版本要求:

  • cryptography >= 44.0:支持 hashes.SM3()algorithms.SM4
  • gmssl >= 3.0:支持 SM2/SM3/SM4 基础操作
⚠️ 关键警告:cryptography 44.x 不支持 ec.SM2() — 它根本没有 SM2 实现。需要 SM2 必须用 gmssl 或 Tongsuo OpenSSL。

cryptography 能做什么(且做得好)

cryptography 库的 SM3 和 SM4 实现经过严格测试,API 设计优雅,是生产环境的首选。

SM3 哈希

SM4-CBC 加密

SM4-GCM 认证加密

PYTHON
def sm4_gcm_encrypt(key: bytes, nonce: bytes, plaintext: bytes, aad: bytes = b'') -> tuple:
    """
    SM4-GCM 加密,返回 (ciphertext, tag)
    aad: 附加认证数据(只认证不加密)
    """
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    # 注意:cryptography 的 SM4-GCM 需要额外处理
    # 截至目前(2026-06),cryptography 尚未直接暴露 SM4-GCM
    # 如需 SM4-GCM,请使用 gmssl 或 Tongsuo 的 Python 绑定
    
    # 推荐方案:使用 gmssl(见后续章节)
    raise NotImplementedError('cryptography 暂不支持 SM4-GCM,推荐使用 gmssl')
📌 现状总结cryptography 目前不支持 SM4-GSM(截至 v48.0)。如需 GCM 模式,使用 gmssl

SM2?cryptography 不支持

PYTHON
# ❌ 以下代码在 cryptography 中不存在
# from cryptography.hazmat.primitives.asymmetric import ec
# ✅ 正确做法:使用 gmssl(见下一节)

gmssl 能做什么(以及它的陷阱)

gmssl 是 Python 中最完整的国密库,提供了 SM2/SM3/SM4 全套支持,但 API 设计有几个"暗坑"。

SM3 哈希

PYTHON
from gmssl import sm3

def gmssl_sm3(data: bytes) -> str:
    """gmssl SM3 哈希,返回小写十六进制字符串"""
    return sm3.sm3_hash(list(data))  # 注意:输入需要是 list(int)

# 使用示例
data = b'Hello'
print(gmssl_sm3(data))
# 输出: 55e12e91650d2fec56ec74e1d3e4ddbfce2ef3a65890c2a19ecf88a307e76a23

SM4 加密(注意 CBC 解密 bug)

🔴 gmssl 陷阱crypt_ecbSM4_DECRYPT 模式(值为 1)下有 bug,可能返回空字节。生产环境 SM4-CBC 解密请使用 cryptography 库。

SM2 密钥生成、签名、验签(完整流程)

这是最复杂的部分。gmssl 的 SM2 API 有几个关键陷阱:

跨库兼容实战:统一国密工具库

基于以上验证,我设计了一个生产级的统一封装,扬长避短

踩坑总结

陷阱现象根因解决方案
cryptography 无 SM2AttributeError: SM244.x 只支持 SM3/SM4用 gmssl 的 SM2
gmssl CBC 解密空 bytesb'' 返回crypt_ecb DECRYPT 模式 bug用 cryptography 的 SM4-CBC
SM2 verify 返回 False验签失败但数据没问题verify 期望哈希值而非原始数据先 SM3(data) 再传入
密钥格式不兼容无法跨库验签两库密钥内部格式不同统一用 gmssl 生成,不混用
hex 输入格式ValueErrorgmssl 的 sign() 接受 bytes.hex() 转换后传入

选型决策树

CODE
需要 SM2 签名?
├── 是 → gmssl(唯一选择)
└── 否 → 
    需要 SM3/SM4?
    ├── 是 → cryptography(更稳定,API 更好)
    └── 否 → 不需要国密

关键结论

  • cryptography 做 SM3/SM4,gmssl 做 SM2 — 这是2026年 Python 国密开发的最优分工
  • 永远不要在两个库之间转换密钥 — 直接使用各自库的原生格式
  • gmssl 的 SM2 verify 是最大的坑 — 参数必须是哈希值,不是原始消息
  • SM4-GCM 目前两库都不完美 — 生产环境建议用 cryptography 的 SM4-CBC + HMAC-SM3 组合,或等待 cryptography 更新
💡 终极建议:如果你的项目重度使用国密,考虑用 pycryptodome 或直接调用 Tongsuo 的 C 扩展,性能更好且行为更一致。

参考来源