使用摘要异步签署 pdf

Sign pdf asynchronously using digest

我正在尝试执行以下设置来签署 pdf,分解为客户端和服务器之间的异步步骤:

  1. 服务器接收 pdf 并计算其摘要。
  2. 服务器将摘要发送给客户端。
  3. 客户端稍后对哈希进行签名。
  4. 客户端将签名发送给服务器。
  5. 服务器将签名嵌入到 pdf 中。

我主要在 and

第二个问题允许我编写大部分代码,但我发现文件的完整性已受到损害。我似乎无法序列化中间 pdf 以稍后嵌入签名(以确保没有时间戳被更改等)。但是从第一道SO题来看,这道题似乎比我想象的要难。真的可以做到吗?

我正在使用 pdfbox。

服务器代码:

        PDDocument document = PDDocument.load(documentFile);
        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("Example User");
        signature.setLocation("Los Angeles, CA");
        signature.setReason("Testing");
        Calendar date = Calendar.getInstance();
        signature.setSignDate(date);
        document.addSignature(signature);

        ExternalSigningSupport externalSigningSupport = document.saveIncrementalForExternalSigning(null);

        byte[] content = IOUtils.toByteArray(externalSigningSupport.getContent());
        MessageDigest md = MessageDigest.getInstance("SHA256", new BouncyCastleProvider());
        byte[] digest = md.digest(content); // this is sent to client

我基本上做的是将该摘要发送给客户端进行签名,然后在服务器上重做上述步骤并设置客户端签名:

        ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(fos);
        externalSigning.setSignature(encodedSignature); // encodedSignature is received from client and computed based on the digest sent by the server

这个设置以文件的完整性被破坏而告终,因为一旦我在服务器上有 encodedSignature 来嵌入它,我就会创建一个新的 PDSignature。有没有办法序列化调用addSignature 后创建的PDDocument,以便稍后在服务器上反序列化它并添加客户端的签名?

What I'm basically doing is sending that digest to the client to sign and then on the server redoing the above steps and setting the client signature

如果您希望上述步骤生成相同的文档,您需要

  • 确保这些步骤的输入是相同的并且
  • 提供相同的修订 ID 种子值。

如果您这样做,上述步骤的输出与您的任务所需的相同。

确保输入相同

您的上述步骤中的一个步骤很容易导致不同的输入:

Calendar date = Calendar.getInstance();
signature.setSignDate(date);

为保证相同的输入,您只需确定 date 一次,并在每次为同一签名交易执行这些步骤时使用该单一值。

提供相同的修订 ID 种子值

按照规范的建议,PDFBox 尝试为每个 PDF 修订提供唯一的 ID。但是,在手头的例子中,执行 上述步骤 时,我们都需要相同的修订 ID。

幸运的是,PDFBox 允许我们提供它用来使修订 ID 足够唯一的种子值。

由于我们不希望在签署同一文档时始终使用相同的修订 ID,而只是在当前签署交易期间使用,因此我们应该仅在同一交易中使用相同的种子值。由于种子值很长,我们可以简单地使用上面已经讨论过的 date 对应的以毫秒为单位的时间,即:

pdDocument.setDocumentId(date.getTimeInMillis());