对这个:
“这是因为我不仅要验证签名的 PDF 是否真实,还要验证它是否与我记录在案的未签名 PDF 相同”
假设您只想知道您在服务器上获得的文档是真实的:
创建签名文档时,您可以选择仅签名文件的一部分或整个文档。然后,您可以使用“整个文档”签名,如果您返回到服务器上的文档是“真实的”(这意味着签名验证成功),那么它肯定是您记录在案的同一文档。
值得一提的是,PDF 签名有两种类型,批准签名和认证签名。来自 Adobe 的 PDF 中的数字签名文档:
(...) 批准签名,即某人签署文件以表示同意、批准或接受。经认证的文档是在文档可供使用时具有由发起人应用的认证签名的文档。发起者指定允许哪些更改;选择允许的三个修改级别之一:
假设您想要匹配您在服务器上获得的某些签名文档,以及数据库上的未签名文档:
对于文件识别,我建议单独处理。一旦可以打开一个文档,就可以从其所有页面的解压缩内容的串联中创建一个哈希(例如 md5),然后将其与来自原始文档的另一个类似的哈希进行比较,(可以生成一次并存储在数据库中)。
我这样做的原因是它将独立于文档上使用的签名类型。即使在 PDF 文件中编辑表单域、添加注释或创建新签名,页面内容也不会被修改,它始终保持不变。
如果您使用的是 iText,您可以使用PdfReader.getPageContent方法获取页面内容的字节数组,并将结果用于计算 MD5 哈希。
Java 中的代码可能如下所示:
PdfReader reader = new PdfReader("myfile.pdf");
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
int pageCount = reader.getNumberOfPages();
for(int i=1;i <= pageCount; i++)
{
byte[] buf = reader.getPageContent(i);
messageDigest.update(buf, 0, buf.length);
}
byte[] hash = messageDigest.digest();
此外,如果服务器接收到一个未签名的文件又回来了签名,则签名可能只涉及文件的一部分而不是全部。在这种情况下,签名摘要可能不足以识别文件。
来自 PDF 规范(我帐户上的粗体部分):
签名是通过计算文档中的数据(或部分数据)的摘要并将摘要存储在文档中来创建的。(...)有两种定义的技术可以计算所有或部分内容的可重现摘要PDF 文件的一部分:
-字节范围摘要是在文件中的字节范围内计算的,由签名字典中的 ByteRange 条目指示。此范围通常是整个文件,包括签名字典但不包括签名值本身(内容条目)。
- 对象摘要 (PDF 1.5) 是通过有选择地遍历内存中对象的子树来计算的,从引用的对象开始,该对象通常是根对象。生成的摘要以及有关其计算方式的信息被放置在签名参考字典 (...) 中。