介绍

让我们面对现实——在2025年,如果你仍然通过电子邮件来回发送文档以获取手写签名,你可能正在浪费时间、金钱,甚至失去客户。How to sign PDF 在 Java 中不再是可有可无的技能;它是金融、医疗、法律服务以及任何重视速度和合规性的行业实现安全、自动化工作流的核心需求。

在 Java 中实现数字签名可能让人望而生畏,但使用 GroupDocs.Signature,你可以将问题拆分为两个逻辑步骤:loading certificates from a keystoresigning the document。本教程将带你逐步完成这两个步骤,解释每个环节的重要性,并提供可直接用于实际应用的生产就绪代码。

阅读完本指南后,你将清晰了解:

  • 如何从 Java keystore 或 Windows 证书存储加载数字证书。
  • 如何使用 GroupDocs.Signature 以编程方式签署 PDF(或其他支持的格式)。
  • 最佳实践安全措施、常见陷阱以及故障排除技巧。

让我们安全地签署你的文档!

快速答案

  • What library handles PDF signing? GroupDocs.Signature for Java.
  • Which Java version is required? JDK 8 或更高;推荐使用 JDK 11+ 以获得更好性能。
  • Can I sign DOCX and XLSX as well? 是的——相同的 API 支持超过 50 种文件类型。
  • Do I need a license for production? 生产环境需要有效的 GroupDocs.Signature 许可证。
  • Is streaming supported for large PDFs? 是的——启用流模式即可在不将整个文件加载到内存的情况下签署数百页的 PDF。

什么是 Java 中的数字签名?

DigitalSignature 概念代表一种加密证明,表明文档是由特定实体创建或批准的。在 Java 中,数字签名将 private key(保密)与 public certificate(共享)结合,以确保已签署文件的真实性、完整性和不可否认性。

为什么使用 GroupDocs.Signature for Java?

GroupDocs.Signature 支持 50+ 输入和输出格式(PDF、DOCX、XLSX、PPTX、HTML、图像等),并且在流模式下可处理高达 200 MB 的文档,内存使用保持在 50 MB 以下。该库还提供内置时间戳、可见签名渲染,并符合 PAdES、XAdES 和 CAdES 标准——成为企业级签名的全功能解决方案。

前置条件

  • Java Development Kit 8 或更高(推荐使用 JDK 11+)。
  • GroupDocs.Signature for Java 版本 23.12 或更新。
  • 一个 .pfx/.p12 格式的 digital certificate 对 Windows 证书存储的访问。
  • IDE,例如 IntelliJ IDEA、Eclipse 或带有 Java 扩展的 VS Code。
  • 对 Java I/O 和 PKI 概念有基本了解。

设置 GroupDocs.Signature for Java

使用 Maven

在你的 pom.xml 文件中添加以下依赖:

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

Maven 将自动拉取该库及所有传递依赖。

使用 Gradle

如果你更喜欢 Gradle,请在 build.gradle 中加入以下代码段:

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

直接下载

你也可以直接从 GroupDocs.Signature for Java releases 下载 JAR 并手动添加到类路径。请记得保持 JAR 更新,以获得安全补丁。

许可证获取步骤

  • Free Trial: 完整功能但有评估限制(水印)。
  • Temporary License: 在不受限制的情况下延长试用期。
  • Purchase: 生产环境必需;许可证可按开发者、站点或 OEM 方式提供。

基本初始化和设置

Signature 类是 GroupDocs.Signature 所有签名操作的主要入口。你创建实例,传入源文件,然后调用 sign 方法。

import com.groupdocs.signature.Signature;

// Initialize Signature object with your document path
Signature signature = new Signature("path/to/your/document.pdf");

Important note: 始终使用 try‑with‑resources 块或显式关闭 Signature 对象,以释放文件句柄并避免内存泄漏。

try (Signature signature = new Signature("path/to/your/document.pdf")) {
    // Your signing code here
} // Auto-closes and releases resources

功能 1:从证书存储加载数字签名

如何在 Java 中加载 keystore?

KeyStore 是 Java 的安全 API,用于存储加密密钥和证书。通过创建 KeyStore 实例、使用密码加载文件并检索私钥条目,可从 Java keystore(.jks.p12.pfx)加载证书。此方法在任何操作系统上均可工作,并让你完全控制证书生命周期。

KeyStore ks = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream("mycert.p12")) {
    ks.load(fis, "password".toCharArray());
}

上面的代码片段演示了核心步骤:实例化 keystore、加载文件流并提供密码。加载后,你可以提取 PrivateKey 以及其关联的证书链用于签名。

导入所需类

首先,导入与 Windows 证书存储和 GroupDocs.Signature 交互所需的类:

import com.groupdocs.signature.domain.signatures.DigitalSignature;
import java.util.ArrayList;
import java.util.List;

创建 LoadDigitalSignatures 类

LoadDigitalSignatures 类封装了扫描 Windows 证书存储并返回可用证书的逻辑。

public class LoadDigitalSignatures {
    public List<DigitalSignature> run() {
        List<DigitalSignature> signatures = new ArrayList<>();
        try {
            // Load digital signatures from 'My' certificate store.
            List<DigitalSignature> signaturesFromStore = DigitalSignature.loadDigitalSignatures(StoreName.My);
            signatures.addAll(signaturesFromStore);
        } catch (Exception e) {
            System.out.println("Error loading certificates: " + e.getMessage());
        }
        return signatures;
    }
}

实际发生了什么?

  • StoreName.My 告诉 Windows 在 Personal(个人)存储中查找,其中包含用户颁发且带有私钥的证书。
  • loadDigitalSignatures() 遍历每个条目,检查是否存在私钥,并将结果包装为 GroupDocs.Signature 可使用的 DigitalSignature 对象。
  • 该方法返回一个包含所有可用证书的 List<DigitalSignature>

何时使用此方法?
适用于 Windows 上的 桌面或内网应用,这些应用的证书通过 Active Directory 集中管理。对于跨平台服务,建议从 .pfx 文件加载(参见上面的 keystore 示例)。

Pro tip: 始终确认返回的列表不为空;列表为空通常表示用户没有签名证书或应用程序没有读取存储的权限。

功能 2:使用数字签名签署文档

如何使用 GroupDocs.Signature 签署 PDF?

为源 PDF 创建 Signature 实例,附加已加载的 DigitalSignature,配置可选的可视外观,然后调用 sign。该方法在保留原始内容的同时写入新的签名文件。生成的签名符合 PAdES 标准,确保在 PDF 查看器中具备法律可采性和防篡改性。

Signature signature = new Signature("sample.pdf");
DigitalSignature sign = loadSignatures.get(0); // choose the first available certificate
SignOptions options = new SignOptions();
options.setSignature(sign);
options.setLeft(100);
options.setTop(100);
options.setWidth(200);
options.setHeight(50);
signature.sign("signed_output.pdf", options);

导入所需类

还需要导入用于签名选项和处理输出文件的类:

import com.groupdocs.signature.Signature;
import com.groupdocs.signature.domain.signatures.DigitalSignature;
import com.groupdocs.signature.options.sign.DigitalSignOptions;
import java.io.File;
import java.security.KeyStore;

创建 SignDocumentWithDigital 类

此类将证书加载与文档签名结合起来,遍历所有可用证书以演示批量签名。

public class SignDocumentWithDigital {
    public void run(String documentPath) {
        // Load digital signatures from the certificate store
        List<DigitalSignature> signatures = new LoadDigitalSignatures().run();
        
        // Counter to create unique output files for each certificate
        int signatureNumber = 0;
        
        for (DigitalSignature digitalSignature : signatures) {
            signatureNumber++;
            String outputFilePath = new File("YOUR_OUTPUT_DIRECTORY", 
                "signed_document_" + signatureNumber + ".pdf").getPath();
            
            try (Signature signature = new Signature(documentPath)) {
                // Configure signing options
                DigitalSignOptions options = new DigitalSignOptions();
                options.setSignature(digitalSignature);
                
                // Optional: Add visible signature appearance
                options.setLeft(100);
                options.setTop(100);
                options.setWidth(200);
                options.setHeight(100);
                
                // Sign the document
                signature.sign(outputFilePath, options);
                System.out.println("Document signed successfully: " + outputFilePath);
                
            } catch (Exception e) {
                System.err.println("Error signing with certificate " + 
                    signatureNumber + ": " + e.getMessage());
            }
        }
    }
}

理解代码流程

  • Loading Certificates: 调用 LoadDigitalSignatures 检索所有可用证书。
  • Iterating Over Certificates: 便于测试或为用户提供签名身份选择。
  • Output Management: 为每个签名文档生成唯一文件名,以避免覆盖已有文件。
  • Signature Configuration: 设置数字证书及可选的可视参数。
  • Signing Execution: sign() 调用创建一个嵌入加密签名的新 PDF。

何时使用此模式?
非常适用于 批处理(例如,夜间签署数千张发票)或 多签名工作流,即多个参与方需要在同一文档上加盖数字签名的场景。

常见问题及解决方案

问题 1:“未找到证书存储”或证书列表为空

Direct answer: 确认 Windows 个人存储中存在带私钥的签名证书,确保应用以具有读取权限的用户账户运行,并在非 Windows 平台上切换为 keystore 加载。

Explanation: 当未找到符合条件的证书时,loadDigitalSignatures() 方法返回空列表。打开 certmgr.msc 确认存在带钥匙图标的证书,并检查存储位置(CurrentUser 与 LocalMachine)。在 Linux/macOS 上,请使用前述的 keystore 加载方式替代存储调用。

问题 2:“访问私钥被拒绝”

Direct answer: 将证书安装到 CurrentUser 存储,通过证书管理器授予用户对私钥的读写权限,并在测试时避免使用不可导出的密钥。

Explanation: 私钥访问错误通常是因为密钥标记为不可导出,或证书位于 LocalMachine 存储而运行进程缺乏权限。请重新导入具有适当权限的证书,或使用你可以控制密码的 .pfx 文件。

问题 3:输出文档损坏或无法打开

Direct answer: 确保输出目录存在且仅包含有效的文件系统字符,并且在签署期间没有其他进程锁定该文件。

Explanation: 如果路径包含非法字符、磁盘已满或源文件在其他地方仍被打开,可能导致损坏。签署前使用 File.getParentFile().mkdirs() 创建目录,并关闭可能占用文件的读取器。

问题 4:大文档的性能问题

Direct answer: 启用流模式 (Signature.setStreaming(true)) 并在确认对证书存储的线程安全访问后,以并行批处理方式处理文档。

Explanation: 将整个 200 页的 PDF 加载到内存可能耗尽堆空间。流式读取和写入文件块,可保持低内存使用。并行处理可加速批处理任务,但需要谨慎处理共享资源。

安全最佳实践

  1. 保护私钥 – 将其存储在硬件安全模块(HSM)或使用 Windows Credential Manager。切勿在源代码中嵌入密码。
  2. 验证证书 – 在签名前检查有效期、链信任、吊销状态和密钥用法扩展。
  3. 使用强算法 – 推荐使用 SHA‑256 与 RSA 2048 位或 ECDSA 256 位密钥;避免 MD5 或 SHA‑1。
  4. 安全传输 – 通过 HTTPS 传输文档,并对签名文件实施基于角色的访问控制。
  5. 审计日志 – 记录签名事件的时间戳、用户 ID 和证书指纹,以满足合规要求。
  6. 密码处理 – 通过安全输入(如 Console.readPassword())获取密码,使用后清除字符数组,且绝不记录密码。

何时使用此方法

理想场景

  • Enterprise Document Management – 自动化签署合同、发票和合规报告。
  • Healthcare – 为满足 HIPAA 审计要求,对电子健康记录(EHR)进行签名。
  • Legal Tech – 为法院提交的文件提供具有法律约束力的签名。
  • Financial Services – 保护贷款协议、KYC 表格和交易记录。

其他方案可能更合适的情况

  • Simple handwritten signatures – 使用基于图像的签名,而非加密签名。
  • Real‑time multi‑party signing – 考虑使用 DocuSign 等 SaaS 电子签名平台进行工作流编排。
  • Blockchain‑anchored signatures – 若需要不可变的链上证明,请使用专用库。
  • Mobile‑first UX – 原生移动 SDK 在 iOS/Android 上可能提供更流畅的用户体验。

常见问题

Q: 签名后我可以验证数字签名吗?
A: 是的——使用 Signature.verify("signed_output.pdf"),它返回一个 VerificationResult,指示签名是否有效、签名者证书详情以及是否有篡改。

Q: GroupDocs.Signature 支持时间戳吗?
A: 绝对支持。你可以通过 options.setTimestampServerUrl("https://tsa.example.com") 附加 TSA(时间戳授权机构)URL,以创建可信的时间戳。

Q: 除了 PDF,我还能签署哪些文件格式?
A: 超过 50 种格式,包括 DOCX、XLSX、PPTX、HTML、PNG、JPEG 和 TIFF。只需在输入和输出路径中更改文件扩展名即可。

Q: 如何对文档进行隐形签名?
A: 省略可视定位方法(setLeftsetTopsetWidthsetHeight)。签名将仅为加密签名,不会在页面上渲染。

Q: 文档大小有限制吗?
A: 启用流模式后,你可以签署大于 500 MB 的文件;内存消耗保持在 100 MB 以下。

结论

现在,你已经拥有使用 GroupDocs.Signature 在 Java 中实现 how to sign PDF 文档的 完整、可用于生产的路线图。从加载证书(无论是来自 Windows 证书存储还是跨平台 keystore)到应用可见和不可见的数字签名,这里的代码片段和最佳实践指南涵盖了构建安全、合规签名工作流所需的一切。

下一步?尝试批量签署真实的发票,集成时间戳以确保法律效力,并探索丰富的 API 以实现自定义签名外观。祝编码愉快,享受加密保护文档带来的安心!


Last Updated: 2026-06-06
Tested With: GroupDocs.Signature for Java 23.12(撰写时的最新版本)
Author: GroupDocs

Documentation | API Reference | Download Latest Version | Purchase License | Free Trial | Support Forum | Temporary License

{< blocks/products/products-backtop-button >} {< /blocks/products/pf/main-wrap-class >} {< /blocks/products/pf/main-container >} {< /blocks/products/pf/tutorial-page-section >}

相关教程