You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用PDFBox保存待签名PDF内容,后续完成签名?

PDFBox支持延迟离线签名吗?

问题描述

已知可通过ExternalSigningSupport实现PDF签名,但该方式要求在同一线程内立即完成签名获取与存储。现希望将待签名的准备内容保存到磁盘,延后在其他线程或后续时间完成签名,请问PDFBox是否提供对应API支持该操作?预期流程示例如下:

预准备阶段

ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(outputStream);
file = saveToFile(externalSigning.getContent());
document.close()

延迟签名阶段(另一线程或次日)

byte[] hash = calculateHash(file);
byte[] signature = sign(hash);
try (PDDocument document = loadPreparedDocumentForSigning(file)) {
    document.setSignature(signature);
}

解答

PDFBox没有直接提供示例中loadPreparedDocumentForSigning这类开箱即用的API,但可以通过手动处理PDF增量更新逻辑实现延迟签名,核心是保存两个关键内容:

  • 待签名的哈希原数据(即externalSigning.getContent()的输出)
  • 签名相关元数据(包括字节范围、签名字段名、预准备PDF的增量结构信息)

1. 预准备阶段:生成待签名文件与元数据

在生成待签名内容时,除了保存待签名字节流,还需记录签名的核心元数据,方便后续定位签名写入位置:

// 加载原PDF文档
try (PDDocument doc = PDDocument.load(new File("original.pdf"))) {
    // 创建签名对象并配置参数
    PDSignature signature = new PDSignature();
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
    signature.setName("签名人");
    signature.setLocation("签名地点");
    signature.setReason("签名原因");
    signature.setSignDate(Calendar.getInstance());
    // 指定哈希算法,需与后续签名阶段一致
    signature.setDigestAlgorithm("SHA-256");
    
    // 将签名添加到文档
    doc.addSignature(signature);
    
    // 生成外部签名支持对象
    ByteArrayOutputStream preparedPdfStream = new ByteArrayOutputStream();
    ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(preparedPdfStream);
    
    // 保存待签名的哈希原数据
    byte[] contentToSign = externalSigning.getContent();
    Files.write(Paths.get("content-to-sign.bin"), contentToSign);
    
    // 保存预准备的PDF文件(已包含签名字典结构,仅预留签名占位符)
    Files.write(Paths.get("prepared-document.pdf"), preparedPdfStream.toByteArray());
    
    // 保存签名元数据:字节范围、签名字段名等,用于后续定位签名写入位置
    Map<String, Object> signatureMeta = new HashMap<>();
    signatureMeta.put("byteRange", signature.getByteRange());
    signatureMeta.put("signatureFieldName", signature.getName());
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("signature-meta.bin"))) {
        oos.writeObject(signatureMeta);
    }
}

2. 延迟签名阶段:完成签名并写入PDF

后续读取保存的文件与元数据,计算哈希并完成签名,最终将签名值写入PDF的指定位置:

// 读取待签名数据并计算哈希
byte[] contentToSign = Files.readAllBytes(Paths.get("content-to-sign.bin"));
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(contentToSign);

// 调用签名服务获取签名值(替换为你的实际签名逻辑)
byte[] signature = sign(hash);

// 读取预准备PDF与签名元数据
try (PDDocument doc = PDDocument.load(new File("prepared-document.pdf"))) {
    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("signature-meta.bin"))) {
        Map<String, Object> signatureMeta = (Map<String, Object>) ois.readObject();
        int[] byteRange = (int[]) signatureMeta.get("byteRange");
        
        // 定位到PDF中签名占位符的位置并写入签名值
        RandomAccessFile raf = new RandomAccessFile("prepared-document.pdf", "rw");
        // byteRange[1]是签名前内容长度,byteRange[2]是占位符起始偏移
        raf.seek(byteRange[1] + byteRange[2]);
        raf.write(signature);
        raf.close();
        
        // 保存最终签名完成的PDF
        doc.save("signed-final.pdf");
    }
}

关键注意事项

  • 必须准确保存签名的byteRange数组,这是定位签名写入位置的核心依据
  • 预准备阶段与签名阶段的哈希算法必须完全一致
  • 预准备PDF已包含完整的签名字典结构,仅预留了签名值的空白占位符

内容的提问来源于stack exchange,提问作者myroch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:57:59