如何使用 Java 对 PDF 进行签名并添加时间戳

当您需要保护合同、发票或任何关键文档免受篡改时,安全地 how to sign PDF 成为首要任务。在本指南中,您将了解如何使用 GroupDocs.Signature for Java 为 PDF 添加数字签名和可信时间戳。该方法离线工作,支持高达 500 MB 的文件,并且只需几行代码。

快速答案

如何使用 Java 对 PDF 进行签名?

加载 PDF,使用您的证书配置 DigitalSignature,可选地从符合 RFC 3161 的 TSA 附加时间戳,然后调用 sign()Signature 对象将签名文件写入磁盘,返回一个 SignResult,告知操作是否成功并列出任何警告。整个端到端流程只需几行 Java 代码,并自动处理哈希、证书验证和时间戳获取。

为什么数字签名重要(以及为何需要时间戳)

数字签名保证 authenticity(签名者身份)和 integrity(文档未被更改)。添加时间戳证明签名在特定时刻存在,即使签名证书随后过期或被吊销,也能保护您。二者共同提供不可否认性——对法律、金融和监管工作流至关重要。

为 Java 设置 GroupDocs.Signature

集成方式

选择您偏好的构建工具:

针对 Maven 用户
将依赖添加到您的 pom.xml

以下 Maven 坐标获取最新稳定版的 GroupDocs.Signature for Java。

<dependency>
    <groupId>com.groupdocs</groupId>
    <artifactId>groupdocs-signature</artifactId>
    <version>23.12</version>
</dependency>

针对 Gradle 用户
将以下行添加到您的 build.gradle

Gradle 将从 Maven Central 解析该库。

implementation 'com.groupdocs:groupdocs-signature:23.12'

直接下载(如果您更喜欢)
前往 GroupDocs.Signature for Java releases 下载 JAR 文件。手动将其添加到项目的 classpath 中。完整 API 参考请参阅 GroupDocs.Signature Documentation。最新构建请查看 Latest Version & Releases

专业提示: Maven 或 Gradle 自动化版本升级和传递依赖,在发布新安全补丁时为您节省时间。

获取许可证

GroupDocs 提供三种授权选项:

  1. Free trial – 评估所有功能且无水印。 Download Trial Version
  2. Temporary license – 30 天全功能开发密钥。
  3. Commercial license – 生产就绪、无限使用。 Buy License

如果您有疑问,社区活跃于 GroupDocs Forum

基本初始化

Signature 是 GroupDocs.Signature 的顶层对象,表示内存中的单个 PDF 文件。创建实例后,所有读写操作都通过它进行。

import com.groupdocs.signature.Signature;

String filePath = "YOUR_DOCUMENT_DIRECTORY/sample.pdf";
final Signature signature = new Signature(filePath);

如何在 Java 中为 PDF 添加数字签名:逐步指南

该过程是线性的:导入类,设置文件路径,创建 Signature 对象,配置带可选时间戳的 DigitalSignature,定义 SignOptions,然后签名并保存。

步骤 1:导入所需类

以下导入为您提供签名配置、定位和时间戳功能的访问权限。

import com.groupdocs.signature.Signature;
import com.groupdocs.signature.domain.enums.HorizontalAlignment;
import com.groupdocs.signature.domain.enums.VerticalAlignment;
import com.groupdocs.signature.domain.signatures.PdfDigitalSignature;
import com.groupdocs.signature.domain.structs.TimeStamp;
import com.groupdocs.signature.options.sign.DigitalSignOptions;

步骤 2:定义文件路径

为输入 PDF、证书(PFX)和输出位置设置路径。请确保证书文件安全;它包含您的私钥。

String filePath = "YOUR_DOCUMENT_DIRECTORY/sample.pdf";
String certificatePath = "YOUR_DOCUMENT_DIRECTORY/certificate.pfx";
String outputFilePath = "YOUR_OUTPUT_DIRECTORY/digitallySignedTimeStamp.pdf";

步骤 3:初始化 Signature 对象

Signature 是所有签名操作的入口。创建它会将 PDF 加载到内存并为后续操作准备 API。

final Signature signature = new Signature(filePath);

步骤 4:配置签名属性和时间戳

DigitalSignature 是将嵌入 PDF 的加密封章。您还可以从受信任的机构附加时间戳。

PdfDigitalSignature pdfDigitalSignature = new PdfDigitalSignature();
pdfDigitalSignature.setContactInfo("Contact Information");
pdfDigitalSignature.setLocation("Location Info");
pdfDigitalSignature.setReason("Signing Reason");

// Configure the TimeStamp with URL, User Id, and Password
TimeStamp timeStamp = new TimeStamp("https://freetsa.org/tsr", "User Id", "Password");
pdfDigitalSignature.setTimeStamp(timeStamp);

我们在演示中使用 FreeTSA(免费时间戳授权机构)。在生产环境中,请选择商业 TSA 以确保正常运行时间和法律效力。

步骤 5:配置数字签名选项

SignOptions 汇总了证书、视觉外观和数字签名的放置设置。

DigitalSignOptions options = new DigitalSignOptions(certificatePath);
options.setPassword("YourCertificatePassword"); // Certificate password
options.setSignature(pdfDigitalSignature); // Attach the PdfDigitalSignature object

// Specify signature alignment (where it appears on the page)
options.setVerticalAlignment(VerticalAlignment.Bottom);
options.setHorizontalAlignment(HorizontalAlignment.Right);

步骤 6:签名并保存文档

SignResult 提供签名操作的结果,包括成功状态和任何警告。

try {
    SignResult signResult = signature.sign(outputFilePath, options);
    System.out.println("Document signed successfully!");
    System.out.println("Output: " + outputFilePath);
} catch (Exception e) {
    throw new RuntimeException("Error during signing process: " + e.getMessage());
}

常见陷阱需避免

1. 证书问题

Problem: “Invalid certificate” 错误。
Fix: 使用 keytool -list -v -keystore your.pfx 验证密码。

keytool -list -v -keystore certificate.pfx -storetype PKCS12

2. 时间戳服务超时

Problem: 联系 TSA 时网络超时。
Fix: 测试连通性(curl -I https://freetsa.org/tsr),添加重试逻辑,或配置备用 TSA。

new File(outputFilePath).getParentFile().mkdirs();

3. 文件权限问题

Problem: 保存时出现 “Access denied”。
Fix: 确保输出目录存在且应用程序具有写权限。

keytool -genkeypair -alias mykey -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore certificate.pfx -validity 365

4. 大型 PDF 的内存问题

Problem: 大文件导致 OutOfMemoryError
Fix: 增加 JVM 堆大小(-Xmx4g)或批量处理文件。

5. 错误的签名位置

Problem: 签名覆盖了已有内容。
Fix: 首先测试对齐设置;如需像素级精确定位,请使用基于坐标的选项。

证书管理技巧

获取开发用证书

使用 Java 的 keytool 生成自签名证书用于测试。

   String certPassword = System.getenv("CERT_PASSWORD");

证书最佳实践

  1. Never hard‑code passwords – 使用环境变量。
  2. Rotate certificates – 在证书过期前进行轮换。
  3. Store private keys – 在安全硬件(HSM)中存储私钥,以用于高安全性应用。
  4. Back up certificates – 在受保护的位置备份证书。
  5. Validate certificates – 在签名前验证证书,以捕获已过期或被吊销的证书。

安全最佳实践

1. 保护私钥

将证书存放在项目目录之外,使用针对不同环境的配置,并在企业部署时考虑使用 HSM。

2. 验证输入 PDF

在签名之前检查是否损坏、是否已有签名、大小限制以及内容合规性。

3. 实施审计日志

记录每一次签名操作的时间戳、用户、文档名称和状态。

try {
    SignResult result = signature.sign(outputFilePath, options);
    logger.info("Document signed: " + filePath + " by " + signerEmail);
} catch (Exception e) {
    logger.error("Signing failed: " + filePath + " - " + e.getMessage());
    // Handle appropriately
}

4. 使用可信的时间戳授权机构

切勿依赖本地系统时间;始终从符合 RFC 3161 的 TSA 请求时间戳。

5. 实施错误处理

捕获异常但不泄露敏感细节。

try {
    signature.sign(outputFilePath, options);
} catch (Exception e) {
    // Log detailed error internally
    logger.error("Signing error: " + e.getMessage(), e);
    // Return generic error to client
    throw new ApplicationException("Unable to sign document. Please try again.");
}

实际使用案例和应用

  1. Contract management systems – 员工以电子方式签署 NDA 和协议;时间戳精确证明每份合同的接受时间。
  2. Financial document processing – 批量签署发票和采购订单,为监管机构提供不可变的审计轨迹。
  3. Educational credential verification – 大学颁发防篡改的成绩单,可通过二维码链接即时验证。
  4. Software license management – 生成带数字签名和时间戳的许可证书,以防伪造。
  5. Regulatory compliance (FDA 21 CFR Part 11, etc.) – 医疗器械公司签署 SOP 和验证报告;时间戳满足不可否认性要求。

性能考虑与优化

内存管理

批量处理大型 PDF,及时关闭 Signature 对象,并在需要时增加堆大小。

时间戳网络优化

池化 HTTP 连接,实现指数退避重试,并缓存时间戳以加速连续签名。

批处理最佳实践

// Pseudo‑code: process a list of PDFs in parallel, limiting to 5 concurrent TSA calls

避免生成过多线程;5‑10 个并发签名在吞吐量和 TSA 负载之间取得平衡。

磁盘 I/O 优化

临时文件使用 SSD,尽量减少读写次数,并在每次签名运行后清理临时产物。

故障排除指南

错误:“Invalid certificate password”

Solution: 使用 keytool -list -keystore your.pfx 验证密码。

ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<SignResult>> futures = new ArrayList<>();

for (String pdfPath : pdfPaths) {
    futures.add(executor.submit(() -> {
        try (Signature sig = new Signature(pdfPath)) {
            return sig.sign(outputPath, options);
        }
    }));
}

// Wait for all to complete
for (Future<SignResult> future : futures) {
    SignResult result = future.get();
    // Process result
}

executor.shutdown();

错误:“Timestamp authority not responding”

Solution: 测试 TSA URL,检查防火墙规则,并添加备用 TSA 逻辑。

keytool -list -v -keystore certificate.pfx -storetype PKCS12

错误:“PDF is already signed”

Solution: 首先检测已有签名;可以添加二次签名或对新副本进行签名。

错误:保存时 “Access denied”

Solution: 确保输出目录存在,应用具有写权限,并且没有其他进程锁定文件。

TimeStamp timeStamp;
try {
    timeStamp = new TimeStamp("https://freetsa.org/tsr", "", "");
} catch (Exception e) {
    // Fallback to alternative TSA
    timeStamp = new TimeStamp("https://alternate-tsa.com/tsr", "", "");
}

错误:OutOfMemoryError

Solution: 增加 JVM 堆,分小批次处理 PDF,或对超大文件使用流式 API。

结论与后续步骤

您现在已经了解如何使用 Java 对 PDF 文件 how to sign PDF,添加可信时间戳,并避免常见陷阱。接下来您可以:

  1. 为多方协议添加多个签名字段。
  2. 使用 GroupDocs.Signature 以编程方式验证签名。
  3. 自定义签名的视觉外观(图像、文本、定位)。
  4. 构建具备队列和监控的强大批量签名服务。

常见问题

Q: 数字签名和电子签名有什么区别?
A: 数字签名使用加密算法验证身份并检测篡改,而电子签名可以仅仅是键入的姓名。

Q: 我需要互联网连接才能签署 PDF 吗?
A: 仅用于时间戳服务;加密签名本身在本地运行。

Q: 已签名的 PDF 以后还能编辑吗?
A: 任何修改都会破坏签名,PDF 阅读器会显示文档已被更改的警告。

Q: 我如何验证已签名的 PDF?
A: 大多数 PDF 阅读器会自动验证;编程上,可使用 GroupDocs.Signature 的验证 API 检查状态、签名者详情和时间戳有效性。

Q: 如果我的证书在我签署文档后过期会怎样?
A: 嵌入的时间戳证明签名在证书仍然有效时创建,保持法律效力。

Q: 我能将其与云存储(S3、Azure Blob 等)一起使用吗?
A: 可以——先将 PDF 下载到临时位置,签名后再上传已签名的版本回云端。

Q: 是否有文件大小限制?
A: 该库可处理高达 500 MB 的 PDF 而无需将整个文件加载到内存;更大的文件可能需要流式处理。

Q: GroupDocs.Signature 商业使用的费用是多少?
A: 价格因部署类型而异;请联系 GroupDocs 销售获取最新报价。提供免费试用和临时许可证供评估。

Q: 这在 Linux 服务器上能运行吗?
A: 当然可以。GroupDocs.Signature for Java 与平台无关,可在任何装有 JRE 的操作系统上运行。


最后更新: 2026-09-05
测试环境: GroupDocs.Signature 23.9 for Java
作者: GroupDocs

相关教程

File outputFile = new File(outputFilePath);
outputFile.getParentFile().mkdirs(); // Create directories if needed

if (!outputFile.canWrite() && outputFile.exists()) {
    throw new IOException("Cannot write to " + outputFilePath);
}