PDF 签名无效,但使用 PDFBox2 验证签名有效 (true)

发布于 2025-01-10 12:17:39 字数 2117 浏览 0 评论 0原文

PDF 示例下载:https://drive.google.com/file/ d/12wv1Pb7gh4vCKOGhX4cZ3aOrLSiOo4If/view?usp=sharing

所以当PDF 在 A.Reader(持续发布)中打开,显示证书无效,因为对此文档进行了更改,导致签名无效。

但我看不出什么/哪里发生了变化。我们自己的应用程序仅添加了签名(证书),该应用程序为数千个其他 PDF 添加了正确的签名。没有执行其他更改。 使用我们自己的代码验证哈希值或使用带有以下代码的 PDFBox2 表示签名有效(true)。

那么,A.Reader 为何抱怨呢?

非常感谢任何帮助,因为我已经把头撞到墙上好几天了......

public static void main(String [] args) throws IOException, CMSException, OperatorCreationException, CertificateException
{
    System.out.println("\nValidate signature in SignatureVlidationTest.pdf; original code.");
    byte[] pdfByte;
    PDDocument pdfDoc = null;
    SignerInformationVerifier verifier = null;
    try
    {
        pdfByte = FileUtils.readFileToByteArray(new File(FOLDEROUT, "102089-5913E701-5EE6-AC3F-7B03-A8D27A7CD9FA.pdf"));  
        pdfDoc = PDDocument.load(new File(FOLDEROUT, "102089-5913E701-5EE6-AC3F-7B03-A8D27A7CD9FA.pdf"));  
       // pdfDoc = Loader.loadPDF(new ByteArrayInputStream(pdfByte));
        PDSignature signature = pdfDoc.getSignatureDictionaries().get(0);

        byte[] signatureAsBytes = signature.getContents();
        byte[] signedContentAsBytes = signature.getSignedContent(pdfByte);
        CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(signedContentAsBytes), signatureAsBytes);
        SignerInformation signerInfo = (SignerInformation) cms.getSignerInfos().getSigners().iterator().next();
        X509CertificateHolder cert = (X509CertificateHolder) cms.getCertificates().getMatches(signerInfo.getSID())
                .iterator().next();
        verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider(new BouncyCastleProvider()).build(cert);

        // result if false
        boolean verifyRt = signerInfo.verify(verifier);
        System.out.println("Verify result: " + verifyRt);
    }
    finally
    {
        if (pdfDoc != null)
        {
            pdfDoc.close();
        }
    }
}

Sample PDF download: https://drive.google.com/file/d/12wv1Pb7gh4vCKOGhX4cZ3aOrLSiOo4If/view?usp=sharing

So when the PDF is opened in A.Reader (Contineous release) it says the Certificate is invalid as Changes have been made to this document that rendered the signature invalid.

But I can't see what/where is changed. Only a Signature (certificate) was added with our own application that adds correct signatures for thousands of other PDFs. No other changes performed.
Verifying the Hash with our own code or using PDFBox2 with following code says the signature is valid (true).

So why is A.Reader complaining?

Any help much appreciated as I'm banging my head to the wall for some days now...

public static void main(String [] args) throws IOException, CMSException, OperatorCreationException, CertificateException
{
    System.out.println("\nValidate signature in SignatureVlidationTest.pdf; original code.");
    byte[] pdfByte;
    PDDocument pdfDoc = null;
    SignerInformationVerifier verifier = null;
    try
    {
        pdfByte = FileUtils.readFileToByteArray(new File(FOLDEROUT, "102089-5913E701-5EE6-AC3F-7B03-A8D27A7CD9FA.pdf"));  
        pdfDoc = PDDocument.load(new File(FOLDEROUT, "102089-5913E701-5EE6-AC3F-7B03-A8D27A7CD9FA.pdf"));  
       // pdfDoc = Loader.loadPDF(new ByteArrayInputStream(pdfByte));
        PDSignature signature = pdfDoc.getSignatureDictionaries().get(0);

        byte[] signatureAsBytes = signature.getContents();
        byte[] signedContentAsBytes = signature.getSignedContent(pdfByte);
        CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(signedContentAsBytes), signatureAsBytes);
        SignerInformation signerInfo = (SignerInformation) cms.getSignerInfos().getSigners().iterator().next();
        X509CertificateHolder cert = (X509CertificateHolder) cms.getCertificates().getMatches(signerInfo.getSID())
                .iterator().next();
        verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider(new BouncyCastleProvider()).build(cert);

        // result if false
        boolean verifyRt = signerInfo.verify(verifier);
        System.out.println("Verify result: " + verifyRt);
    }
    finally
    {
        if (pdfDoc != null)
        {
            pdfDoc.close();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

叫嚣ゝ 2025-01-17 12:17:40

第一个 PDF 修订版中的交叉引用损坏

第一个修订版末尾的交叉引用表如下所示:

xref
0 19
0000000000 65535 f
0000000018 00000 n
0000000348 00000 n
0000000422 00000 n
0000000481 00000 n
0000000776 00000 n
0000003138 00000 n
0000032630 00000 n
0000033308 00000 n
0000033489 00000 n
0000033723 00000 n
0000033932 00000 n
0000056202 00000 n
0000056645 00000 n
0000056837 00000 n
0000070988 00000 n
0000071312 00000 n
0000071521 00000 n
0000071543 00000 n
20 26
0000071844 00000 n
0000080069 00000 n
0000080373 00000 n
0000080556 00000 n
0000097791 00000 n
0000097813 00000 n
0000097833 00000 n
0000097853 00000 n
0000097876 00000 n
0000097899 00000 n
0000097922 00000 n
0000097945 00000 n
0000097968 00000 n
0000097991 00000 n
0000098014 00000 n
0000098037 00000 n
0000098059 00000 n
0000098083 00000 n
0000104407 00000 n
0000104444 00000 n
0000104483 00000 n
0000104565 00000 n
0000104704 00000 n
0000104728 00000 n
0000111035 00000 n
0000111072 00000 n
48 1
0000111098 00000 n
50 2
0000111296 00000 n
0000113066 00000 n

如您所见,它由对象编号为 0..18、20..45、48 和 50 的多个小节组成。 51.特别是对象编号 19、46、47 和 49 没有映射。

这是不允许的,原因有两个:

  • 对于从未增量更新的文件,尤其是对于每个 PDF 文件的第一个修订版,交叉引用部分应仅包含一个小节,其对象编号从 0 开始。

  • 交叉引用表(包括原始交叉引用部分和所有更新部分)应为每个对象编号包含一个条目,从 0 到文件中定义的最大对象编号,甚至如果该范围内的一个或多个对象编号确实文件中实际上并未出现。

(ISO 32000-1 第 7.5.4 节“交叉引用表”)

因此,常规 PDF 的第一个交叉引用表必须仅包含一个小节。即使这不是必需的,也不允许与未映射的对象编号存在间隙。

通常,Adobe Reader 会忽略违反这些要求的行为,但在签名验证的情况下,它会更加严格。通常,这会出现在相关 PDF 已签名,然后添加一些任意增量更新的情况下。

例如,我获取了您的第一个修订版(文件的前 114510 个字节)并对它们进行了签名,然后将它们扩展为 LTA:

仅签名、签名和扩展
仅签名签名并扩展

这一直是有关堆栈溢出的多个问题的主题:

其他问题

很可能还有其他问题有待发现不过,在您的示例 PDF 中。如上所述,第一个修订版中的交叉引用通常只会在向签名 PDF 添加增量更新后才会出现问题。您的示例 PDF 并非如此。因此,我预计其中还会有其他奇怪的事情。

备注

一些额外的观察结果:

  • 从第一个修订版开始,文档信息字典中有许多额外条目:SIG_PAGESIG_LLXSIG_LLYSIG_URXSIG_URY。 IMO 这不是合适的地方,尽管符合要求的读者可以在文档信息字典中存储自定义元数据,但他们可能不会在那里存储私有内容或结构信息。此类信息应存储在文档目录中。(ISO 32000-1 第 14.3.3 节“文档信息字典”)IMO 这些条目看起来像是您工作流程私有的处理指令,而不是公共利益的元数据。< /p>

  • 您的签名字典包含一个R值。自 PDF 1.5 起,不应使用此条目,并且该信息应存储在 Prop_Build 字典中。(ISO 32000-1 表 252“签名字典中的条目”)

Broken Cross References in First PDF Revision

The cross reference table at the end of your first revision looks like this:

xref
0 19
0000000000 65535 f
0000000018 00000 n
0000000348 00000 n
0000000422 00000 n
0000000481 00000 n
0000000776 00000 n
0000003138 00000 n
0000032630 00000 n
0000033308 00000 n
0000033489 00000 n
0000033723 00000 n
0000033932 00000 n
0000056202 00000 n
0000056645 00000 n
0000056837 00000 n
0000070988 00000 n
0000071312 00000 n
0000071521 00000 n
0000071543 00000 n
20 26
0000071844 00000 n
0000080069 00000 n
0000080373 00000 n
0000080556 00000 n
0000097791 00000 n
0000097813 00000 n
0000097833 00000 n
0000097853 00000 n
0000097876 00000 n
0000097899 00000 n
0000097922 00000 n
0000097945 00000 n
0000097968 00000 n
0000097991 00000 n
0000098014 00000 n
0000098037 00000 n
0000098059 00000 n
0000098083 00000 n
0000104407 00000 n
0000104444 00000 n
0000104483 00000 n
0000104565 00000 n
0000104704 00000 n
0000104728 00000 n
0000111035 00000 n
0000111072 00000 n
48 1
0000111098 00000 n
50 2
0000111296 00000 n
0000113066 00000 n

As you can see it consists of multiple subsections with object numbers 0..18, 20..45, 48, and 50..51. In particular there is no mapping for object numbers 19, 46, 47, and 49.

This is disallowed for two reasons:

  • For a file that has never been incrementally updated, and so in particular for the first revision of each PDF file, the cross-reference section shall contain only one subsection, whose object numbering begins at 0.

  • The cross-reference table (comprising the original cross-reference section and all update sections) shall contain one entry for each object number from 0 to the maximum object number defined in the file, even if one or more of the object numbers in this range do not actually occur in the file.

(ISO 32000-1 section 7.5.4 "Cross-Reference Table")

Thus, the first cross reference table of a regular PDF must consist of only a single subsection. And even if that was not required, gaps with unmapped object numbers are not allowed.

Normally Adobe Reader ignores violations of these requirements, but in the context of signature validation it is stricter. Usually this shows in situations where the PDF in question is signed and then some arbitrary incremental update is added.

For example, I took your first revision (the first 114510 bytes of your file) and signed them and then extended them to LTA:

only signedsigned and extended
only signedsigned and extended

This has been the topic in multiple questions here on stack overflow:

Additional Problems

There most likely are other issues still to be found in your example PDF, though. As mentioned above, cross references like in your first revision usually only cause problems after adding an incremental update to the signed PDF. That is not the case for your example PDF. Thus, I would expect other oddities in it.

Remarks

Some additional observations:

  • Starting from the first revision you have a number of extra entries in the document information dictionary: SIG_PAGE, SIG_LLX, SIG_LLY, SIG_URX, and SIG_URY. IMO this is not the appropriate place, although conforming readers may store custom metadata in the document information dictionary, they may not store private content or structural information there. Such information shall be stored in the document catalogue instead. (ISO 32000-1 section 14.3.3 "Document Information Dictionary") IMO those entries look like processing instructions private to your work flow, not metadata of public interest.

  • Your signature dictionary contains a R value. Since PDF 1.5 this entry shall not be used, and the information shall be stored in the Prop_Build dictionary. (ISO 32000-1 table 252 "Entries in a signature dictionary")

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文