如何用Python结合ECDSA与p12证书实现PDF数字签名
用ECDSA P12证书在Python中给PDF签名的解决方案
核心问题澄清
- P12证书容器支持ECDSA:你的P12文件只要包含ECDSA私钥、对应签名证书以及完整的证书链(可选但推荐),就具备PDF数字签名所需的全部要素——私钥用于生成ECDSA签名,证书和链用于验证身份与信任链。
- endesive库支持ECDSA:endesive底层依赖
cryptography库,完全支持ECDSA算法签名,不需要更换库,只需确保你的P12密钥是ECDSA类型即可。
代码调整与优化
针对你提供的代码,以下是关键调整点和完整优化版本:
优化后的代码
import datetime from cryptography.hazmat.primitives.serialization import pkcs12 from endesive.pdf import cms def firmar(contraseña, certificado, pdf): # 处理签名时间(UTC时间,符合PDF规范) signing_date = datetime.datetime.utcnow().strftime("D:%Y%m%d%H%M%S+00'00'") # 签名配置参数 sig_config = { "aligned": 0, "sigflags": 3, "sigflagsft": 132, "sigpage": 0, "sigbutton": True, "sigfield": "Signature1", "auto_sigfield": True, "sigandcertify": True, "signaturebox": (470, 640, 570, 840), # 坐标顺序:x1(左下), y1(左下), x2(右上), y2(右上) "signature": "Aquí va la firma", "contact": "hola@ejemplo.com", "location": "Ubicación", "signingdate": signing_date, "reason": "Razón", } # 加载P12证书(新版本cryptography无需指定backend) private_key, cert, cert_chain = pkcs12.load_key_and_certificates( certificado.read(), contraseña.encode("ascii") ) # 验证密钥类型是否为ECDSA(可选,用于调试) from cryptography.hazmat.primitives.asymmetric import ec if isinstance(private_key, ec.EllipticCurvePrivateKey): print("检测到ECDSA私钥,符合要求") else: print("当前P12中的密钥不是ECDSA类型,请确认证书") # 读取原PDF内容 original_pdf = pdf.read() # 生成ECDSA签名:指定sha256哈希,自动匹配ECDSA签名算法 signed_data = cms.sign( original_pdf, sig_config, private_key, cert, cert_chain, # 传入证书链,而非空列表 "sha256" ) # 拼接原PDF与签名数据,生成带签名的完整PDF signed_pdf = original_pdf + signed_data return signed_pdf
关键调整说明
- 移除冗余Backend参数:新版本
cryptography已默认使用最优后端,无需手动指定backends.default_backend()。 - 修复签名坐标:PDF的坐标原点在左下角,原代码的
signaturebox顺序错误,调整为左下到右上的坐标才能正确显示签名框。 - 传递证书链:将P12中加载的
cert_chain传入cms.sign,确保验证方可以完整验证证书信任链。 - ECDSA密钥验证:添加了密钥类型检查,帮助你确认P12文件是否确实包含ECDSA私钥。
- 简化时间处理:直接使用当前UTC时间,原代码的12小时偏移无必要。
使用示例
# 调用示例 if __name__ == "__main__": with open("tu_certificado.p12", "rb") as p12_file, \ open("original.pdf", "rb") as pdf_file: signed_pdf = firmar("tu_contraseña", p12_file, pdf_file) with open("firmado.pdf", "wb") as output_file: output_file.write(signed_pdf)
常见问题排查
- P12密钥类型错误:如果打印显示不是ECDSA密钥,说明你的P12文件是RSA类型的,需要重新生成ECDSA类型的P12证书。
- 签名验证失败:确保证书链完整(P12包含中间证书),且签名哈希算法与密钥类型匹配(ECDSA推荐使用SHA256/SHA384)。
- 签名框不显示:检查
signaturebox的坐标是否符合PDF页面尺寸(A4页面默认范围是0,0到595,842)。
内容的提问来源于stack exchange,提问作者eleded
相关产品推荐
相关产品推荐

