游乐游手机版
首页/编程语言/文章详情

如何使用Python对PDF文档进行数字签名的详细方法

时间:2026-07-20 06:43
基于PKI技术,使用Python为PDF添加可见或隐藏数字签名,实现完整性校验与身份认证。通过PdfOrdinarySignatureMaker签署,结合时间戳确保长期验证,并调用VerifySignature验证签名有效性,适合批量自动化场景,保障文档安全可靠,防止篡改与伪造,提升验证效率。

在合同、发片、报告等文件的流转过程中,如何确认文档确实由某个签署人发出,并且在签署之后没有被篡改,是一个经常遇到的问题。数字签名就是为了解决这类问题而设计的:它基于 PKI(公钥基础设施)技术,把签署人的身份与文档内容绑定在一起,一旦文档被修改,签名的校验就会失败。

相比手动在 Adobe Acrobat 等工具里逐份签署,用程序批量处理签名更适合需要自动化的场景,例如系统自动生成的合同、每日结算单、审批通过后自动盖章的流程文件等。本文围绕如何用 Python 为 PDF 添加可见签名、隐藏签名和带时间戳的签名,并对已签名文档进行验证,展开详细介绍。

数字签名解决的问题

用程序处理数字签名,通常出于以下几方面的考虑:

  • 完整性校验:签名后文档内容一旦被改动,验证会立即失效,这就像给文档加了一把锁,任何篡改迹象都会暴露无遗。
  • 身份认证:签名基于持有人的证书(PFX/PKCS#12 文件),能证明文档由特定主体签署,而不是冒名顶替。
  • 批量自动化:审批系统、结算系统可以在文件生成时自动完成签署,无需人工逐份介入,效率提升明显。
  • 可长期验证:结合时间戳服务,即使签署证书后续过期,也能证明签署行为发生在证书有效期内,不影响后续验证。

环境准备

处理 PDF 签名,这里推荐使用 Spire.PDF for Python,通过 pip 安装即可:

 复制代码pip install Spire.Pdf

除了库本身,还需要一个用于签署的数字证书文件,通常是 .pfx(或 .p12)格式,并配有对应的密码。证书可以由 CA 机构签发,在测试阶段也可以使用自签名证书先跑通流程。

添加可见的数字签名

可见签名会在页面上绘制一个签名框,显示签署人、原因、位置等信息,适合需要让阅读者直观看到“已签署”状态的场景。

最关键的是 PdfOrdinarySignatureMaker 对象——它接收 PDF 文档、证书文件路径和证书密码,负责完成签署运算。签名的外观则通过 PdfSignatureAppearance 单独配置。

 复制代码from spire.pdf import *# 加载 PDF 文档
doc = PdfDocument()
doc.LoadFromFile("SampleB_1.pdf")# 使用证书文件和密码创建签名生成器
signatureMaker = PdfOrdinarySignatureMaker(doc, "Jerry.pfx", "e-iceblue")# 配置签名的元数据
signature = signatureMaker.Signature
signature.Name = "Jerry"
signature.ContactInfo = "028-12345678"
signature.Location = "Chengdu"
signature.Reason = "文档负责人签署"# 配置签名外观
appearance = PdfSignatureAppearance(signature)
appearance.NameLabel = "签署人: "
appearance.ContactInfoLabel = "联系方式: "
appearance.LocationLabel = "地点: "
appearance.ReasonLabel = "原因: "
appearance.SignatureImage = PdfImage.FromFile("logo.png")
appearance.GraphicMode = GraphicMode.SignImageAndSignDetail# 在第一页指定位置绘制签名框:x, y, 宽, 高
signatureMaker.MakeSignature("Signer:", doc.Pages.get_Item(0), 90.0, 550.0, 270.0, 90.0, appearance)doc.Sa veToFile("DigitalSignature.pdf")
doc.Dispose()

这里有几个关键点值得说明:

  • Signature 属性上设置的 NameReasonLocation 等信息会被写入签名对象,PDF 阅读器在展示签名详情时会读取它们,所以填写准确的信息非常重要。
  • GraphicMode 决定签名框里显示什么内容。SignImageAndSignDetail 表示同时显示图片(如公司 Logo)和文字信息;也可以只显示图片或只显示文字,根据实际需求灵活选择。
  • MakeSignature 的坐标参数以点(point)为单位,原点在页面左下角。最后四个数字分别对应签名框的左边界、下边界、宽度和高度。注意,这个坐标系统与常见的屏幕坐标相反,放置时记得换算一下,不然签名框可能会跑到纸张外面去。

添加隐藏签名

有些场景并不希望在页面上出现签名框,只需要在文档层面完成签署以保证完整性即可。这时可以创建不带外观参数的隐藏签名。

 复制代码from spire.pdf import *doc = PdfDocument()
doc.LoadFromFile("SampleB_1.pdf")# 创建签名生成器
signatureMaker = PdfOrdinarySignatureMaker(doc, "Jerry.pfx", "e-iceblue")# 只传签名字段名称,不设置外观,即为隐藏签名
signatureMaker.MakeSignature("signName")doc.Sa veToFile("AddInvisibleSignature.pdf")
doc.Dispose()

隐藏签名同样具备防篡改能力,阅读器仍能在签名面板中检测到它,只是页面上不显示任何可见标记。这类签名适合后端自动化处理,比如系统内部批量签署结算单,无需在每份文档上都留下视觉印记。

添加带时间戳的签名

数字证书都有有效期,如果证书过期后,签名验证可能会失效。但如果你在签署时引入了可信时间戳服务(TSA),就能证明“签署行为发生在证书仍然有效的时间点”,这样即使证书后来过期,签名依然有效。Security_PdfSignature 提供了 ConfigureTimestamp 方法来指定 TSA 服务地址。

 复制代码from spire.pdf import *doc = PdfDocument()
doc.LoadFromFile("SampleB_1.pdf")# 创建带页面引用的签名对象
signature = Security_PdfSignature(doc, doc.Pages.get_Item(0), "Jerry.pfx", "e-iceblue", "signature")# 设置签名框的位置和大小
signature.Bounds = RectangleF(PointF(90.0, 550.0), SizeF(180.0, 90.0))
signature.NameLabel = "Digitally signed by: Jerry"
signature.Reason = "确保文档真实性"# 限制签署后允许的操作:仅允许填写表单,禁止其他修改
signature.DocumentPermissions = PdfCertificationFlags.AllowFormFill.value | PdfCertificationFlags.ForbidChanges.value# 配置可信时间戳服务
signature.ConfigureTimestamp("https://freetsa.org/tsr")doc.Sa veToFile("SignedByTimestamp.pdf")
doc.Dispose()

DocumentPermissions 是这里一个很实用的属性,它以证书化签名的方式限定签署后允许的操作。示例中的组合表示“允许继续填写表单字段,但禁止其他任何改动”,这很适合审批完成后仍需收集信息、又要防止内容被篡改的表单类文档。

验证已签名的文档

拿到一份签名文档后,可以遍历其表单中的签名字段,逐个校验签名是否有效。

 复制代码from spire.pdf import *doc = PdfDocument()
doc.LoadFromFile("SignedDocument.pdf")# 访问文档表单
formWidget = PdfFormWidget(doc.Form)if formWidget.FieldsWidget.Count > 0:
    for i in range(formWidget.FieldsWidget.Count):
        field = formWidget.FieldsWidget.get_Item(i)
        if isinstance(field, PdfField):
            signatureField = PdfSignatureFieldWidget(field)
            # 用字段全名验证对应签名
            valid = doc.VerifySignature(signatureField.FullName)
            print("签名有效" if valid else "签名无效")doc.Dispose()

VerifySignature 方法会重新计算文档内容的摘要,并与签名中记录的值进行比对。如果文档在签署之后被改动过,哪怕只是删除了一个空格,返回结果也是无效。这才是数字签名“防篡改”承诺的最终落地。

实用建议

  • 证书密码不要硬编码:示例中为了演示直接写在代码里,实际项目应该从环境变量或密钥管理服务读取,这是基本的安全常识。
  • 坐标系统:签名框坐标以页面左下角为原点,向上为 y 正方向,与常见的屏幕坐标相反,放置时记得换算一下,避免签名框出现在意料之外的位置。
  • 签名顺序:一份文档可以有多个签名。后签署的签名会把先前的签名内容一并纳入校验范围,因此签署顺序会影响验证逻辑,多签场景下需要提前规划好流程。
  • 及时释放资源:处理完成后调用 Dispose() 释放文档占用的句柄,避免批量任务中文件被占用导致后续操作失败。

总结

本文围绕 PDF 数字签名的常见需求,介绍了可见签名、隐藏签名、带时间戳签名以及签名验证四种操作。它们的核心都建立在证书文件与文档内容的绑定之上:PdfOrdinarySignatureMaker 负责普通签署,Security_PdfSignature 在此基础上补充了时间戳和权限控制,而 VerifySignature 则完成校验闭环。

在此基础上,可以进一步结合表单字段、文档权限和长期验证(LTV)等能力,构建出适合合同管理、审批流转等场景的自动化签署方案。

来源:https://juejin.cn/post/7662753651803701302
上一篇LeetCode 139 单词拆分:从暴力回溯到动态规划完全背包 下一篇Linux系统优化PHP-FPM性能的方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
ThinkPHP框架搭建从安装到运行完整教程
编程语言 · 2026-07-21

ThinkPHP框架搭建从安装到运行完整教程

使用ThinkPHP搭建开发框架需完成环境准备、项目初始化、配置调整与启动验证四步。环境要求PHP7 1以上及关键扩展,通过Composer创建项目,配置数据库与调试模式,最后运行内置服务器验证。生产环境应改用Nginx或Apache。

Redisson自定义注解与Spring Boot Starter自动配置方法
编程语言 · 2026-07-21

Redisson自定义注解与Spring Boot Starter自动配置方法

先说结论,整个流程的核心就三件事:用注解声明锁的行为,用切面拦截来执行加解锁,用Starter自动把客户端和切面Bean都注册好。无需手动注册,无需额外配置,统一由Starter自动化完成,大幅简化Redisson分布式锁在Spring Boot中的集成。Redisson 自定义注解与 Spring

ThinkPHP5.1结合Swoole实现毫秒级定时任务调度
编程语言 · 2026-07-21

ThinkPHP5.1结合Swoole实现毫秒级定时任务调度

在ThinkPHP5 1项目中,使用Swoole实现毫秒级定时任务调度,需在onWorkerStart回调中启动异步定时器,且仅由worker_id=0的Worker执行。前置条件包括swoole扩展4 8 0以上、协程模式优先用CoroutineTimer。通过自定义Artisan命令统一管理,适用于亚秒级响应、动态启停的深度业务场景。

ThinkPHP跨服务器迁移部署操作步骤
编程语言 · 2026-07-21

ThinkPHP跨服务器迁移部署操作步骤

ThinkPHP跨服务器迁移需确保环境一致性:数据库导出加--no-definer并避免触发器干扰;PHP版本及扩展(openssl、mbstring等)严格对齐;Web服务器root指向public目录,配置伪静态规则;runtime目录清空并确保可写, env文件设置权限与访问控制。

TP6.0电子合同实现 PDF生成与水印签名技术栈
编程语言 · 2026-07-21

TP6.0电子合同实现 PDF生成与水印签名技术栈

TP6 0仅作为Web框架,电子合同需依赖外部工具链。生成PDF可选用tcpdf或dompdf,前者适合结构化合同,后者适合HTML转PDF。水印需在每页底层绘制矢量文字并锁定图层,防止被移除。签名需结合CA认证和数字签名,仅插入图片不具有法律效力。