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 时,它说证书无效,因为对该文档进行了更改,导致签名无效。
但我看不到 what/where 已更改。我们自己的应用程序只添加了一个签名(证书),它为数以千计的其他 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 修订中的交叉引用损坏
第一次修订结束时的交叉引用 table 如下所示:
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 文件的第一次修订,cross-reference节只包含一个小节,其对象编号从0开始。
cross-referencetable(包括原始cross-reference部分和所有更新部分)应包含每个对象编号的一个条目来自0 到文件中定义的最大对象编号,即使此范围内的一个或多个对象编号实际上并未出现在文件中。
(ISO 32000-1 第 7.5.4 节“Cross-Reference Table”)
因此,常规 PDF 的第一个交叉引用 table 必须仅包含一个小节。即使这不是必需的,也不允许存在未映射对象编号的间隙。
通常 Adobe Reader 会忽略违反这些要求的行为,但在签名验证的情况下会更严格。通常这会出现在对相关 PDF 进行签名然后添加一些任意增量更新的情况下。
例如,我对您的第一个修订版(文件的前 114510 个字节)进行了签名,然后将其扩展为 LTA:
only signed
signed and extended
这是关于堆栈溢出的多个问题的主题:
- After add LTV signature got corrupted for some pdf file
其他问题
不过,您的示例 PDF 中很可能还有其他问题有待发现。如上所述,在您的第一个修订版中的交叉引用通常只会在向已签名的 PDF 添加增量更新后 引起问题。您的示例 PDF 情况并非如此。因此,我希望其中有其他奇怪之处。
备注
一些额外的观察:
从第一次修订开始,文档信息字典中有许多额外的条目:SIG_PAGE, SIG_LLX, SIG_LLY, SIG_URX,以及 SIG_URY。 IMO 这不是合适的地方,虽然符合要求的读者可能会在文档信息字典中存储自定义元数据,但他们可能不会在那里存储私人内容或结构信息。此类信息应存储在文档目录中。(ISO 32000-1 第 14.3.3 节“文档信息字典”)IMO 这些条目看起来像是您的工作流程私有的处理指令,而不是 [=113 的元数据=]兴趣
您的签名字典包含一个 R 值。由于 PDF 1.5 不应使用此条目,信息应存储在 Prop_Build 字典中。 (ISO 32000-1 table 252 "条目在 a签名词典")
示例 PDF 下载:https://drive.google.com/file/d/12wv1Pb7gh4vCKOGhX4cZ3aOrLSiOo4If/view?usp=sharing
因此,当在 A.Reader(连续发布)中打开 PDF 时,它说证书无效,因为对该文档进行了更改,导致签名无效。
但我看不到 what/where 已更改。我们自己的应用程序只添加了一个签名(证书),它为数以千计的其他 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 修订中的交叉引用损坏
第一次修订结束时的交叉引用 table 如下所示:
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 文件的第一次修订,cross-reference节只包含一个小节,其对象编号从0开始。
cross-referencetable(包括原始cross-reference部分和所有更新部分)应包含每个对象编号的一个条目来自0 到文件中定义的最大对象编号,即使此范围内的一个或多个对象编号实际上并未出现在文件中。
(ISO 32000-1 第 7.5.4 节“Cross-Reference Table”)
因此,常规 PDF 的第一个交叉引用 table 必须仅包含一个小节。即使这不是必需的,也不允许存在未映射对象编号的间隙。
通常 Adobe Reader 会忽略违反这些要求的行为,但在签名验证的情况下会更严格。通常这会出现在对相关 PDF 进行签名然后添加一些任意增量更新的情况下。
例如,我对您的第一个修订版(文件的前 114510 个字节)进行了签名,然后将其扩展为 LTA:
only signed | signed and extended |
---|---|
这是关于堆栈溢出的多个问题的主题:
- After add LTV signature got corrupted for some pdf file
其他问题
不过,您的示例 PDF 中很可能还有其他问题有待发现。如上所述,在您的第一个修订版中的交叉引用通常只会在向已签名的 PDF 添加增量更新后 引起问题。您的示例 PDF 情况并非如此。因此,我希望其中有其他奇怪之处。
备注
一些额外的观察:
从第一次修订开始,文档信息字典中有许多额外的条目:SIG_PAGE, SIG_LLX, SIG_LLY, SIG_URX,以及 SIG_URY。 IMO 这不是合适的地方,虽然符合要求的读者可能会在文档信息字典中存储自定义元数据,但他们可能不会在那里存储私人内容或结构信息。此类信息应存储在文档目录中。(ISO 32000-1 第 14.3.3 节“文档信息字典”)IMO 这些条目看起来像是您的工作流程私有的处理指令,而不是 [=113 的元数据=]兴趣
您的签名字典包含一个 R 值。由于 PDF 1.5 不应使用此条目,信息应存储在 Prop_Build 字典中。 (ISO 32000-1 table 252 "条目在 a签名词典")