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


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

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

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

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

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

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

  1. 如何从签名的PDF中提取PDF数据,不包括签名?
  2. 或者,如何从未签名的PDF生成哈希?
  3. 与2.一起,如何从PDF签名中提取解密的哈希?

希望这很清楚,我没有错过任何地方的观点!


1792
2017-08-22 13:44


起源

@Lie Ryan,也许你可以在这个项目上找到你的解决方案 portablesigner.sourceforge.net。 - detunized


答案:


对这个:

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

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


7
2017-10-18 15:03



Why would you want to do that?,验证文档实际上是由服务器生成的并不是重点。在我的情况下,用户可能正在下载多个未签名的文档,然后他们必须在这些文档上放置一个批准签名(如果被拒绝),那么他们必须将签名的文档上传到正确的位置。我希望能够检查用户是否犯了错误并交换了签名文档(即如果他们将错误的文档上传到错误的位置)。 - Lie Ryan
@Lie Ryan是的,我理解,我正在回答关于那个案子的原始问题,而不是你的问题。在您的情况下,请参阅我的答案中的“文档标识”部分。 - yms


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

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

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

  3. 验证签名和文档证书。

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

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

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

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

5
2017-10-16 15:20



这根本不能解决问题;服务器生成多个动态文档。检查文档是否由服务器生成并且以其他方式未被禁用是不够的,我需要检查文档是否是特定文档的签名版本。 - Lie Ryan
您需要做的就是添加 什么 (表单字段除外)唯一标识文档的文档类型并检查其是否存在。用户无法在不使认证失效的情况下更改该唯一标识符。 - Kevin Stricker
我懂了。他们想看看它是否是同一个文档,但是不同的哈希值,所以他们知道它已被更改。 - Lee Louviere