能否用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
相关产品推荐
相关产品推荐

