PDF 签名无效,但带有 PDFBox2 的 Verfiy 签名有效 (true)

PDF Signature invalid but Verfiy Signature with PDFBox2 is valid (true)

提问人:user1391606 提问时间:2/25/2022 最后编辑:mkluser1391606 更新时间:2/28/2022 访问量:1014

问:

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

因此,当在A.Reader(连续版本)中打开PDF时,它说证书无效,因为对此文档进行了更改,使签名无效。

但我看不出有什么/在哪里发生了变化。我们自己的应用程序只添加了一个签名(证书),该应用程序为数千个其他 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();
        }
    }
}
Java PDF PDF框 签名

评论

0赞 Tilman Hausherr 2/26/2022
也许是页面结构中的某些内容;我唯一能找到的是,在原始页面中,在修订后的页面中,Adobe可能认为这是不同的数字?/MediaBox[ 0 0 595.3 841.9]/MediaBox [0 0 595.300 841.900 ]
0赞 user1391606 2/26/2022
嗨,感谢您的反馈。我需要检查它,但我对此表示怀疑,因为在其他 PDF 中我们有类似的更改,并且不会发生此错误。我想一定是别的什么......
1赞 gettalong 2/26/2022
因此,我可以看到 PDF 有三个修订版本,基本版本和版本版本通过增量更新进行修订。最后一个修订版是包含签名的修订版。由于在此基础上没有执行其他增量更新,因此无法进行任何更改。我的猜测是Adobe Reader抱怨其他事情。使用的证书似乎缺少“数字签名”密钥用法扩展,因此这可能是 Reader 的问题。实际上,当您查看证书并转到详细信息时,会显示一个红色感叹号表示密钥用法。
0赞 Tilman Hausherr 2/27/2022
我切断了签名部分并使用 PDFBox 示例签名,并遇到了同样的问题。然后我切断了,这样只有一个修订版(显示一些私人数据)并遇到同样的问题。而且我在增量段中没有 /MediaBox。
1赞 mkl 2/27/2022
文档的第一个修订版有一个损坏的交叉引用表。众所周知,这会导致验证期间出现问题。通常,这些问题只有在原始文档有增量更新时才会浮出水面,但从本质上讲,这意味着这个损坏的原始 PDF 不适合签名。

答:

2赞 mkl 2/28/2022 #1

第一个 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:

仅签名 签名和扩展
only signed signed and extended

这是关于堆栈溢出的多个问题的主题:

其他问题

但是,在您的示例 PDF 中很可能还会发现其他问题。如上所述,像第一次修订版中的交叉引用通常只在向已签名的 PDF 添加增量更新后才会引起问题。您的示例 PDF 并非如此。因此,我预计其中还有其他奇怪之处。

言论

一些额外的观察结果:

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

  • 签名字典包含一个 R 值。从 PDF 1.5 开始,不得使用此条目,信息应存储在 Prop_Build 字典中。 (ISO 32000-1 表 252“签名词典中的条目”)