iText 如何修复签名 pdf 的交叉引用 table

iText how to fix cross reference table for signed pdf

我正在使用 itext 7 签署具有 2 个签名字段并且 itext 正在打印的 pdf Error occurred while reading cross reference table. Cross reference table will be rebuilt. 导致错误的行是

new PdfSigner(sourceDoc, signedFile, new StampingProperties().useAppendMode());

通常当我遇到这个错误时,我会通过将 pdf 复制到新的代码来修复它:

sourceDocument.copyPagesTo(firstPage, sourceDocument.getNumberOfPages(), targetDocument, firstPage);

然后在 targetDocument 上签名。它修复了交叉 table 问题,但它不复制现有签名。 所以我添加了

sourceDocument.copyPagesTo(..., new PdfPageFormCopier());

它确实复制了签名,但结果它们是无效的(因为文档哈希已更改并且它影响了签名?)

是否有机会修复文档,使签名完好无损并附加另一个签名?

我不控制正在签名的 pdf,因为它是客户端将它发送给我并且他设法在其他地方签名(可能通过使用旧版本的 itext 或打开 pdf?)尽管存在交叉引用问题(或者是什么导致了问题)。

导致 itext7 出现问题的参考 table

xref
0 60
0000000000 65535 f 
0000175044 00000 n 
0000000000 65536 n 
0000095603 00000 n 
0000000015 00000 n 
0000086011 00000 n 
0000020754 00000 n 
0000019785 00000 n 
0000019501 00000 n 
0000000212 00000 n 
0000020091 00000 n 
0000057104 00000 n 
0000054819 00000 n 
0000054543 00000 n 
0000020921 00000 n 
0000055414 00000 n 
0000085852 00000 n 
0000083751 00000 n 
0000083466 00000 n 
0000057253 00000 n 
0000084295 00000 n 
0000000000 65536 n 
0000095805 00000 n 
0000000000 65536 n 
0000164295 00000 n 
0000095937 00000 n 
0000096014 00000 n 
0000107919 00000 n 
0000121074 00000 n 
0000120425 00000 n 
0000120265 00000 n 
0000108058 00000 n 
0000120701 00000 n 
0000121221 00000 n 
0000138109 00000 n 
0000137022 00000 n 
0000136863 00000 n 
0000121317 00000 n 
0000137511 00000 n 
0000138253 00000 n 
0000146202 00000 n 
0000145690 00000 n 
0000145530 00000 n 
0000138341 00000 n 
0000145904 00000 n 
0000164151 00000 n 
0000162977 00000 n 
0000162818 00000 n 
0000146341 00000 n 
0000163510 00000 n 
0000000000 65536 n 
0000174805 00000 n 
0000164534 00000 n 
0000164611 00000 n 
0000174666 00000 n 
0000175345 00000 n 
0000175115 00000 n 
0000175251 00000 n 
0000175398 00000 n 
0000175459 00000 n 
trailer
<</Size 60/Info 59 0 R/ID [<2dda6a23fc5d97b5d4e670c84756e5dd><262a30912f11d84731389759bd75bbf9>]/Root 58 0 R>>
startxref
175602
%%EOF

itext 在读取第 0000000000 65536 n 行后调用错误 file position {0} cross reference entry in this xref subsection.(从上数第三个)

问题

最终您发布了有问题的交叉引用 table。立刻映入眼帘的是多个这样的词条:

0000000000 65536 n

它们的意思是代号为65536的对应对象位于偏移量0处。

这在两个方面是无效的:

  • 代号超出允许范围:

    ISO 32000-2, section 7.5.4 "Cross-reference table"
    The maximum generation number is 65,535

    (有人可能认为原始 PDF 的制作者试图在依赖有效值的 PDF 处理器中造成 int16 溢出。您确定 PDF 来自 trustable 来源吗?)

  • 偏移量 0 处的 PDF 有 %PDF-x.y 行而不是对象。即使有人认为 PDF 处理器应该将该行视为常规注释并跳过它,下一个间接对象也只能匹配所有这些条目中的一个,对于其他条目,对象编号将不正确。

顺便说一句:这与 (with 0000000000 00000 n entries) and the work-around provided below is essentially the same as provided in 到那个问题的问题有关。不同之处在于您的案例中额外的无效代号。

修复交叉引用表以保持签名完整

您在评论中分享了签名的带符号字节范围和 PDF 的大小:

First signature /ByteRange [0 177104 196050 1580 ]
Second signature /ByteRange [0 212340 277878 23120 ]
File size: My linux shows 300,998 bytes

检查第二个签名的字节范围可以看到它的第二个范围包含 23120 个字节,从偏移量 277878 开始,即直到文件末尾 (277878 + 23120 = 300998)。

因此, 现有文件中的任何修复工作都会更改哈希值。任何使用 appended 新交叉引用 table 的修复工作至少会增加最新修订版的大小,从而使第二个签名不再覆盖其修订版。无论哪种方式,Adobe Reader 都会抱怨。

尽管如此仍要签名

如果您觉得冒险并且想要使用损坏的交叉引用条目签署该文档,您可以让 iText 相信您的 PdfReader 加载的文件毕竟没有损坏。您可以通过覆盖相应的 getter 方法而不是

来实现
PdfReader sourceDoc = new PdfReader(SOURCE);

使用

PdfReader sourceDoc = new PdfReader(SOURCE) {
    @Override
    public boolean hasRebuiltXref() {
        return false;
    }

    @Override
    public boolean hasFixedXref() {
        return false;
    }
};

实例化您的 PdfReader。现在应该可以在附加模式下登录了,请参阅我在测试中的 proof-of-concept testSignBrokenXrefForced class SignBrokenPdf.

当心,你现在得到的结果也是错误的,任何签名或 PDF 验证器都可能拒绝它。仅当您无法说服您的客户生成有效的 PDF 时才这样做。即使那样你也不应该。如果转发已签名的 PDF 导致某些后续处理器出现问题,您尤其要负责。