【免费下载链接】core
A modern PDF library for TypeScript. Parse, modify, and generate PDFs with a clean, intuitive API.
LibPDF是一款面向 TypeScript 的现代 PDF 库,本文带你从零基础快速搞懂 PDF 数字签名的核心:如何用LibPDF 数字签名功能,在 PAdES 标准的B-B、B-T、B-LT、B-LTA 四级合规等级中挑选合适级别,三步完成签名并实现长期归档验证。
📌 什么是 PDF 数字签名?为什么重要?
数字签名就像给电子文档盖上了"防伪印章",它一次性提供三重保障:
| 能力 | 作用 | 通俗理解 |
|---|---|---|
| 认证 Authentication | 验证是谁签的名 | "这章是谁盖的?" |
| 完整性 Integrity | 检测签署后是否被篡改 | "盖章后文件动过吗?" |
| 不可抵赖 Non-repudiation | 签名人无法否认签过 | "你说你没签?签名不会骗人" |
LibPDF 的签名实现遵循PAdES(PDF Advanced Electronic Signatures)标准,核心入口是pdf.sign(),完整的签署流程由 pdf-signature.ts 中的PDFSignature类管理,签名级别的类型定义见 types.ts:
export type PAdESLevel = "B-B" | "B-T" | "B-LT" | "B-LTA";🗂️ PAdES 四级合规等级一张表看懂
| 等级 | 名称 | 额外包含什么 | 典型用途 |
|---|---|---|---|
| B-B | 基础签名 | 仅签名 + 证书 | 内部流转的简单签署 |
| B-T | 带时间戳 | + RFC 3161 签名时间戳 | 需证明"何时签署"的合同 |
| B-LT | 长期验证 | + 证书链 + OCSP/CRL 吊销数据 | 证书过期后仍可验证的场景 |
| B-LTA | 长期归档 | + 覆盖全文的文档时间戳 | 法律、档案等 10 年+ 保存要求 |
💡记忆口诀:B-B 是"能验",B-T 是"能验+有可信时间",B-LT 是"证书过期了也能验",B-LTA 是"永远能验"。
级别越高,验证数据的"保质期"越长。index.mdx 中的官方指南也推荐:有法律效力的场景,至少使用 B-T。
🎯 如何选择合适的签名等级?
三个问题帮你快速决策:
- 文档需要保存多久?只需近期使用 →B-B足够
- 需要证明签署时间点吗?合同、审批流 →B-T
- 证书几年后会过期,还需要能验证吗?归档、审计、长期合规 →B-LT或B-LTA
⚠️ 注意:B-LT 及以上级别需要从网络获取 OCSP/CRL 吊销数据,签名时机器必须能联网;数据会被直接嵌入 PDF,之后离线也能验证。
✍️ 快速上手:用 P12 证书三步完成基础签名
最常用的签名方式是 PKCS#12 证书文件(.p12/.pfx)。以测试证书 test-signer-aes256.p12 为例,完整示例可参考 sign-with-p12.ts:
import { PDF, P12Signer } from "@libpdf/core"; const pdf = await PDF.load(await readFile("contract.pdf")); // ① 从 .p12 文件创建签名器 const signer = await P12Signer.create(await readFile("cert.p12"), "password"); // ② 签名(默认 B-B,可指定 level 与签名原因) const { bytes, warnings } = await pdf.sign({ signer, reason: "合同审批", location: "上海", }); // ③ 增量保存,保留已有签名 await writeFile("signed.pdf", bytes);几个实用细节:
- 签名原因与位置:
reason、location、contactInfo会写入签名元数据 - 签名域自动创建:指定
fieldName时,字段不存在会自动创建(见 add-signature-field.ts) - 占位符机制:签名前会先预留字节空间,默认 12KB,不够时会抛出
PlaceholderError,相关实现见 placeholder.ts
⏰ 升级到 B-T:给签名加一个可信时间戳
签名里自带的"签署时间"只是签名者主机的本地时钟,不具备法律效力。B-T 级别通过RFC 3161 时间戳解决:由独立的时间戳权威(TSA)对签名做哈希并盖章,证明"签名在某一时刻已存在"——即使日后证书过期或被吊销,这个时间点依然可信。
const tsa = new HttpTimestampAuthority("http://timestamp.digicert.com"); await pdf.sign({ signer, level: "B-T", timestampAuthority: tsa, // 传入时间戳服务即可 });时间戳权威接口定义见 types.ts,HTTP 客户端实现在 timestamp.ts。生产环境建议使用证书供应商自家的 TSA 服务。
🔒 升级到 B-LT:嵌入长期验证数据(LTV)
B-T 的"短板"是:几年后证书链过期,验证器可能无法再验证签名。B-LT 的答案是——把所有验证证据直接打包进 PDF:
- 完整证书链(签名者 + 中间证书 + 根证书)
- 每个证书的OCSP 响应(在线吊销状态查询)
- 每个证书的CRL(证书吊销列表)
这些数据统一写入 PDF 的DSS(Document Security Store)增量更新。核心实现分两块:
| 模块 | 职责 |
|---|---|
| gatherer.ts | 从 CMS 结构中提取证书、OCSP、CRL 等全部 LTV 数据 |
| dss-builder.ts | 将 LTV 数据组织成 DSS 增量更新并写回 PDF |
await pdf.sign({ signer, level: "B-LT", timestampAuthority: tsa, // B-LT 隐含包含时间戳 });吊销数据的网络抓取由 revocation.ts 中的DefaultRevocationProvider完成;若证书链含 AIA 扩展,aia.ts 还会自动补全中间证书。
📦 升级到 B-LTA:长期归档的最高合规等级
B-LTA =B-LT + 文档时间戳。它在文档末尾追加一个/DocTimeStamp签名,其 ByteRange 覆盖整个文档,相当于给"全部签名 + 全部验证数据"再加一道时间封印:
- 保护已嵌入的 DSS 吊销数据不被事后篡改
- 支持未来"重新加盖时间戳",实现无限期验证
- 满足 10 年以上的档案保存合规要求
await pdf.sign({ signer, level: "B-LTA", timestampAuthority: tsa, });可运行示例见 sign-archival.ts。更灵活的做法是先把 B-T 签好,最后再统一"封存":
// 步骤1:为所有签名补全验证数据(B-T → B-LT) await pdf.addValidationData(); // 步骤2:追加归档文档时间戳(→ B-LTA) await pdf.addTimestamp({ timestampAuthority: tsa, longTermValidation: true });👥 多人会签流程:先 B-T 快签,最后一步归档
多人签署场景的最佳实践(详见 multiple-signatures.ts 与 index.mdx):
- 每位签署人用 B-T 级别签署——快,且每个签名者无需各自做吊销查询
- 所有人签完后,调用
addValidationData()一次性为全部签名收集 LTV 数据(同一证书颁发机构只做一次查询,避免重复请求) - 调用
addArchivalData({ timestampAuthority })一步完成B-LTA 封存:收集验证数据 + 添加归档时间戳 + 内嵌时间戳自身的验证数据
const { bytes, signatureCount } = await pdf.addArchivalData({ timestampAuthority: tsa });⚠️注意:
addTimestamp()等方法依赖增量保存。若文档无法增量保存(如线性化文档),会抛出SignatureError拒绝执行——因为完整重写会作废所有已有签名。若中途失败,请丢弃当前实例,用PDF.load()从最后已知良好的字节重新加载。
🚀 不止 P12:浏览器与云 KMS 也能签
签名器实现Signer接口即可(接口定义见 types.ts),LibPDF 内置三种:
| 签名器 | 适用场景 | 源码 |
|---|---|---|
P12Signer | 本地证书文件,最常用 | signers/p12.ts |
CryptoKeySigner | 浏览器 Web Crypto API 环境 | signers/crypto-key.ts |
GoogleKmsSigner | 企业级 HSM 托管密钥(Google Cloud KMS) | signers/google-kms.ts |
// 浏览器端:直接用 Web Crypto 的 CryptoKey 签名 const signer = new CryptoKeySigner(privateKey, certificateDer, "RSA", "RSASSA-PKCS1-v1_5"); await pdf.sign({ signer });✅ 安全最佳实践与常见问题排查
五条安全习惯🛡️
- 保护好私钥:绝不外泄
.p12文件或密码 - 使用受信任的 CA 证书:自签名证书会在阅读器中触发警告
- 法律效力场景至少 B-T:附带可信时间戳
- 坚持增量保存:保住之前的所有签名
- 签完即验:用 Adobe Reader 等阅读器确认签名有效
常见报错速查🩺
| 现象 | 原因 | 解决 |
|---|---|---|
| Adobe 提示"未知签名" | 证书不受信任 | 换用 CA 签发的证书 |
| 编辑后签名失效 | 文档被完整重写 | 使用增量保存 |
| 时间戳失败 | TSA 服务器不可达 | 检查网络与防火墙 |
| 提示证书过期 | 证书已到期 | 续期证书后重新签署 |
当前已知限制(来自 官方指南):
- LibPDF 目前支持签名但不支持验证签名
- B-LT/B-LTA 需要联网获取 OCSP/CRL
- 签名为不可见签名(不显示在页面上),如需手写签名效果,可用绘图 API 先行绘制
📚 进阶资源清单
| 资料 | 路径 |
|---|---|
| 数字签名官方指南 | content/docs/guides/signatures/index.mdx |
| Google Cloud KMS 签名指南 | content/docs/guides/signatures/google-kms.mdx |
| 签名核心 API | src/api/pdf-signature.ts |
| 签名类型与选项 | src/signatures/types.ts |
| 全部签名示例(B-B 到 B-LTA) | examples/07-signatures/ |
| 长期验证(LTV)实现 | src/signatures/ltv/ |
选对 PAdES 等级,用对签名流程——现在你已经可以像专业电子签章平台一样,用 LibPDF 完成从简单签署到法律级长期归档的全部工作了 🎉
【免费下载链接】core
A modern PDF library for TypeScript. Parse, modify, and generate PDFs with a clean, intuitive API.
相关推荐
LibPDF签名长期有效性详解:B-LT/B-LTA档案级验证与DSS存储完全指南
LibPDF签名长期有效性详解:B LT/B LTA档案级验证与DSS存储完全指南 LibPDF 是一个面向 TypeScript 的现代 PDF 库,提供解析
5个实用的健康科技项目创意:从睡眠追踪到医疗影像分析
5个实用的健康科技项目创意:从睡眠追踪到医疗影像分析 你是否曾经想过,科技如何让我们的健康生活变得更加智能和便捷?ideas for projects peop
如何理解B树与B+树:数据库索引的终极数据结构指南
如何理解B树与B+树:数据库索引的终极数据结构指南 在数据库领域,高效的数据检索离不开优秀的索引结构。B树与B+树作为数据库索引的核心数据结构,能够显著提升查询
文档教程知识库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考