提问人:user1391606 提问时间:2/25/2022 最后编辑:mkluser1391606 更新时间:2/28/2022 访问量:1014
PDF 签名无效,但带有 PDFBox2 的 Verfiy 签名有效 (true)
PDF Signature invalid but Verfiy Signature with PDFBox2 is valid (true)
问:
示例 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();
}
}
}
答:
第一个 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:
这是关于堆栈溢出的多个问题的主题:
- 为什么这两个 itext 7 签名和验证的文档之一在 Adobe DC 阅读器中无效?
- 使用CERTIFIED_NO_CHANGES_ALLOWED在追加模式下对串联的 PDF 进行签名
- 在签名中添加 LTV 会使 pdf 使用 C 无效#
- 添加某些 pdf 文件的 LTV 签名损坏后
- PDF/A-3A 文档的 PAdES LTV 签名产生无效签名
其他问题
但是,在您的示例 PDF 中很可能还会发现其他问题。如上所述,像第一次修订版中的交叉引用通常只在向已签名的 PDF 添加增量更新后才会引起问题。您的示例 PDF 并非如此。因此,我预计其中还有其他奇怪之处。
言论
一些额外的观察结果:
从第一个修订版开始,文档信息字典中有许多额外的条目:SIG_PAGE、SIG_LLX、SIG_LLY、SIG_URX 和 SIG_URY。IMO 这不是合适的地方,尽管符合要求的读者可以在文档信息字典中存储自定义元数据,但他们可能不会在那里存储私人内容或结构信息。此类信息应存储在文件目录中。(ISO 32000-1 第 14.3.3 节“文档信息字典”)IMO 这些条目看起来像是您的工作流程私有的处理指令,而不是公共利益的元数据。
签名字典包含一个 R 值。从 PDF 1.5 开始,不得使用此条目,信息应存储在 Prop_Build 字典中。 (ISO 32000-1 表 252“签名词典中的条目”)
评论
/MediaBox[ 0 0 595.3 841.9]
/MediaBox [0 0 595.300 841.900 ]