Python 签名库实战:hashlib、pycryptodome、cryptography

做签名验证,Python 里绕不开这三个库。我最早接触的是 hashlib,后来项目需要非对称签名才补了 pycryptodome,再后来发现 cryptography 才是真正省心的选择。你想想看,一个合格的签名流程至少需要哈希、密钥管理、签名生成和验证,这三个库正好各有所长,但用不好就踩坑。

核心观点:没有万能库,只有合适的场景。hashlib 解决「摘要」;pycryptodome 提供传统算法(RSA/DSA)的完整实现;cryptography 是现代签名接口的首选,尤其适合与 HSM 或对安全策略敏感的场景。

1. hashlib —— 地基级哈希工具

签名之前先做哈希,这个道理你应该懂。hashlib 是 Python 标准库,我几乎每天都会用到它。

import hashlib

data = b"Hello, this is a message to sign."
sha256_hash = hashlib.sha256(data).digest()
print(sha256_hash.hex())

但这里有一个坑:hashlib 只做哈希,不做签名。我见过不少新人直接拿 MD5 当“签名”用,那是胡来。另外,SHA-1 在 2017 年 Google 成功碰撞后就已经被业界拉黑了。我个人习惯:新项目一律用 SHA-256 或 SHA-384。

我曾经踩过的坑:一个旧系统迁移时,发现历史数据用的是 hashlib.md5(),而且签名没加盐。结果碰撞攻击直接让系统信任了伪造消息。从那以后,我坚持在哈希前混入随机盐值,并且使用 hashlib.pbkdf2_hmac 做密钥派生。

2. pycryptodome —— 老牌加密套件

如果你需要 RSA、DSA、ECC 等经典签名算法,pycryptodome 是不错的选择。它包含了签名、加密、哈希、随机数等全套工具箱。

from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

key = RSA.generate(2048)
message = b"Authorize transaction #12345"
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)
print(signature.hex())

不过它的 API 风格偏底层,你需要自己管理密钥对象和哈希实例。我个人建议:如果是快速原型或者需要兼容 OpenSSL 生成的密钥,用 pycryptodome 很顺。但注意它的安装 pip install pycryptodome 在 Windows 上偶尔会报编译错误,换用 pycryptodomex 可以解决。

一个小技巧:pycryptodome 的签名验证默认不检查哈希算法是否匹配。你需要在验证时传入相同的哈希对象,否则验证会意外失败。我曾经因为 SHA256 拼写大小写问题 debug 了半小时。

3. cryptography —— 现代签名接口首选

如果让我在 2025 年推荐一个签名库,我会毫不犹豫选 cryptography。它的 Fernet 和 Hazmat 层分别对应开箱即用和底层控制。对于签名验证,我们通常使用 Hazmat 层的 ecdsa 或 rsa。

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

message = b"Sign this critical record"
signature = private_key.sign(
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)

# 验证
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256()
    )
    print("Signature valid!")
except:
    print("Invalid signature")

注意这里没有显式做哈希,cryptography 会在内部自动处理消息的哈希操作。相比之下,pycryptodome 需要你先哈希再签名。我个人更偏爱 cryptography 的设计,它减少了出错的机会。而且它的密钥序列化(PEM、DER、PKCS#8)非常规范,和 HSM 导出的密钥配合完美。

经验之谈:真实项目中曾出现 signature 长度不固定的情况,因为 padding 参数不一致导致验证失败。建议你在项目中固定一组参数(PSS + SHA256 + MAX_LENGTH),不要随意改动。

4. 三库对比一览

特性 hashlib pycryptodome cryptography
是否标准库 是 第三方 第三方
哈希支持 核心功能(SHA1/2/3) 丰富(含 BLAKE2、Skein) 标准 + 扩展(通过 backends)
非对称签名 不支持 RSA, DSA, ECDSA, Ed25519 RSA, DSA, ECDSA, Ed25519, X448
API 风格 极简 底层,需手动管理对象 分层:Fernet(高层) / Hazmat(底层)
与 HSM 配合 需要额外封装 可通过 PKCS#11 桥接 推荐(支持 OpenSSL 引擎)
安全审计 基础 良好,但历史漏洞偏多 优秀,持续维护

5. 签名库生态关系图

我画了一张图,帮你理清这三个库在签名验证流程中的位置。你可以看到,hashlib 是底层依赖,pycryptodome 和 cryptography 则支持直接调用签名算法,最终产出签名或验证结果。

Python 签名库生态关系图 Python 签名库协作流程 原始消息 hashlib SHA-256 / SHA-3 等 pycryptodome RSA / ECDSA 签名 cryptography Fernet / Hazmat 接口 签名 / 验证结果 ⚡ 底层哈希均匀依赖 两者二选一即可

6. 真实项目的选型建议

我参与过的金融级签名系统,最终是这么选的:

  • 单纯计算消息摘要(比如做 HMAC 密钥派生):用 hashlib 就够了,零依赖。
  • 需要和遗留的 Java / C++ 服务互签:用 pycryptodome,因为它对 PKCS#1 v1.5 支持最好,兼容性高。
  • 新项目,特别是要对接 HSM(PKCS#11):用 cryptography 的 hazmat 层,配合 openssl 引擎,省心省力。

额外提醒:无论用哪个库,永远不要在日志里打印完整的签名或私钥。我曾经在 debug 时直接 print(private_key),结果日志被三线运维拿走,两周后系统私钥自动轮换才解决问题。