【问题标题】:Comparing a signed PDF to an unsigned PDF using document hash使用文档哈希将签名的 PDF 与未签名的 PDF 进行比较
【发布时间】:2012-08-17 23:02:57
【问题描述】:

经过大量的谷歌搜索后,我开始怀疑我是否在某种程度上遗漏了数字签名的意义。

这基本上是我认为我原则上应该能够做到的,我希望 iTextSharp 允许我这样做:

我正在用 C# 和 .NET 编写并使用 iTextSharp 解析 PDF 文件。我有一个未签名的 PDF 文件,以及同一文件的签名版本。

我知道数字签名从根本上对 PDF 数据进行哈希处理,使用私钥对其进行加密,然后验证过程的一部分是使用公钥对其进行解密,并确保在再次进行哈希处理时结果与 PDF 数据匹配。

除此之外,我还想获取这个解密的文档哈希,并将其与我的未签名 PDF 生成的文档哈希进行比较。这是因为我不仅要验证签名的 PDF 是否真实,还要验证它是否与我记录在案的未签名 PDF 相同。我想我也可以通过将 PDF 数据(没有签名)与我记录的 PDF 数据进行比较来做到这一点。

我目前还没有弄清楚如何做到这一点!即:

  1. 如何从已签名的 PDF 中提取除签名之外的 PDF 数据?
  2. 或者,如何从未签名的 PDF 生成哈希?
  3. 除了 2.,我如何从 PDF 签名中提取解密的哈希?

希望这很清楚,并且我没有在某处错过重点!

【问题讨论】:

标签: c# pdf itextsharp


【解决方案1】:

验证签名 PDF 完整性的策略:

  1. 首先不要发送未签名的 PDF。使用 iText(适用于 Linux 友好型应用程序的 Java 版本),使用 CERTIFIED_FORM_FILLING 签名并certify 文档。

  2. 让最终用户将其签名添加到表单字段并将其发回。可以这样做,因为对表单的更改不会破坏文件认证。

  3. 验证签名和文件认证。

您应该能够从 iText 文档中了解如何执行所有这些操作: http://itextpdf.sourceforge.net/howtosign.html

您只需将文档元数据与原件进行比较,即可验证经认证的文档是否与原件相同。想到这个标题是一个潜在的好候选人。

要从 pdf 中获取标题以使用 iText 进行比较,您只需使用以下代码:

PdfReader reader = new PdfReader("AsignedPDF.pdf");
string s = reader.Info["Title"];

【讨论】:

  • 这根本解决不了问题;服务器生成多个动态文档。仅检查文档是否由服务器生成并且未被任何其他方式篡改是不够的,我需要检查该文档是否是特定文档的签名版本。
  • 您需要做的就是添加任何东西(除了表单域),以唯一地标识文档类型到文档并检查它是否存在。用户无法更改该唯一标识符而不使认证失效。
  • 我明白了。他们想查看它是否是同一个文档,但哈希值不同,因此他们知道它已被更改。
【解决方案2】:

关于这个:

"这是因为我不仅要验证签名的 PDF 是 真实的,而且它与我记录在案的未签名 PDF 相同”

假设您只想知道您在服务器上获取的文档是真实的:

创建签名文档时,您可以选择仅签名文件的一部分或整个文档。然后,您可以使用“整个文档”签名,如果您返回到服务器上的文档是“真实的”(这意味着签名验证成功),那么它肯定是您记录在案的同一文档。

值得一提的是,PDF签名有两种类型,批准签名和认证签名。来自文档Digital Signatures in PDF from Adobe

(...) 批准签名,其中有人签署文件以显示 同意、批准或接受。经认证的文件是具有 发起人在文件时应用的证明签名 可以使用了。发起者指定允许哪些更改; 选择允许的三个修改级别之一:

  • 没有变化
  • 仅限表格填写
  • 表格填写和评论

假设您想将服务器上的某个已签名文档与数据库上的未签名文档进行匹配:

对于证件识别,我建议单独处理。一旦可以打开一个文档,就可以从其所有页面的解压缩内容的串联中创建一个哈希(例如 md5),然后将其与来自原始文档的另一个类似的哈希进行比较,(可以生成一次并存储在数据库中)。

我这样做的原因是它将独立于文档上使用的签名类型。即使在 PDF 文件中编辑表单域、添加注释或创建新签名,页面内容也不会被修改,它始终保持不变。

如果你使用的是iText,你可以通过PdfReader.getPageContent方法获取页面内容的字节数组,并将结果用于computing a MD5 hash

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 条目指示。这 range 通常是整个文件,包括签名字典 但不包括签名值本身(内容条目)。

-对象摘要 (PDF 1.5) 通过有选择地在内存中遍历对象的子树来计算,从引用的对象开始, 这通常是根对象。生成的摘要以及 有关如何计算的信息,被放置在签名中 参考字典 (...)。

【讨论】:

  • Why would you want to do that?,验证文档是否真的由服务器生成并不是重点。在我的情况下,用户可能正在下载多个未签名的文档,然后他们必须在这些文档上放一个批准签名(或者不放,如果它被拒绝),然后他们必须将签名的文档上传到正确的位置。我希望能够检查用户是否可能犯了错误并交换了签名的文档(即他们是否将错误的文档上传到了错误的地方)。
  • @Lie Ryan 是的,我明白了,我回答的是关于那个案子的原始问题,而不是你的问题。你的情况,请看我回答的“证件识别”部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
  • 2015-06-20
  • 1970-01-01
  • 2019-04-03
相关资源
最近更新 更多