使用文档哈希将签名的PDF与未签名的PDF进行比较

时间:2012-08-22 13:44:27

标签: c# pdf itextsharp

经过广泛的谷歌搜索,我开始怀疑我是否在某种程度上忽略了数字签名的重点。

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

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

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

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

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

  1. 如何从签名的PDF中提取PDF数据,不包括签名?
  2. 或者如何从未签名的PDF生成哈希?
  3. 与2.一起,如何从PDF签名中提取解密的哈希?
  4. 希望这很清楚,我在某处并没有错过这一点!

2 个答案:

答案 0 :(得分:7)

关于这个:

  

“这是因为我不仅要验证签名的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文件的内容:

     

-A 字节范围摘要是在文件中的字节范围内计算的,由签名字典中的ByteRange条目指示。这个   range通常是整个文件,包括签名字典   但不包括签名值本身(内容条目)。

     

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

答案 1 :(得分:5)

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

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

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

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

  4. 您应该能够从iText文档中找出如何完成所有这些操作: http://itextpdf.sourceforge.net/howtosign.html

    您需要做的就是验证经过认证的文档与原始文档是否相同才能将文档元数据与原始元数据进行比较。作为潜在的好候选人,脑海中浮现出这个标题。

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

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