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 则支持直接调用签名算法,最终产出签名或验证结果。
6. 真实项目的选型建议
我参与过的金融级签名系统,最终是这么选的:
- 单纯计算消息摘要(比如做 HMAC 密钥派生):用
hashlib就够了,零依赖。 - 需要和遗留的 Java / C++ 服务互签:用
pycryptodome,因为它对 PKCS#1 v1.5 支持最好,兼容性高。 - 新项目,特别是要对接 HSM(PKCS#11):用
cryptography的hazmat层,配合openssl引擎,省心省力。
额外提醒:无论用哪个库,永远不要在日志里打印完整的签名或私钥。我曾经在 debug 时直接 print(private_key),结果日志被三线运维拿走,两周后系统私钥自动轮换才解决问题。