如何检查 PDF 是否具有任何类型的数字签名

How to check if a PDF has any kind of digital signature

我需要了解 PDF 是否具有任何类型的数字签名。我必须管理 巨大的 PDF,例如每个 500MB,所以我只需要找到一种方法将未签名的和已签名的 PDF 分开(这样我就可以将刚刚签名的 PDF 发送到管理它们的方法)。到目前为止发现的任何程序都涉及尝试通过例如提取证书。 Bouncycastle 库(在我的例子中,Java):如果它存在,则 pdf 已签名,如果它不存在或引发异常,则不是(原文如此!)。但这显然是 time/memory 消耗,而不是资源浪费实施的一个例子。

有没有什么快速的独立于语言的方式,例如打开 PDF 文件,读取第一个字节并找到表明该文件已签名的信息? 或者,有没有参考手册详细说明如何在内部制作PDF?

提前致谢

您将希望使用 PDF 库而不是尝试自己实现所有这些,否则您将陷入处理线性化文档、过滤器、增量更新、对象流、交叉引用流的变体的困境, 等等。

关于参考material;根据我的粗略搜索,Adobe 似乎不再向所有人提供其版本的 ISO 32000:2008 规范,尽管该规范主要是将 PDF v1.7 Reference manual 翻译成符合 ISO 的语言。

假设 PDF v1.7 参考,最相关的部分将是 8.7(数字签名)、3.6.1(文档目录)和 8.6(交互式表单)。

基本过程将是:

  1. 阅读 'Perms' 和 'AcroForm' 个条目的文档目录。
  2. 阅读 'Perms' 词典中的 'DocMDP'、'UR' 或 'UR3' 条目。如果存在这些条目,那么您很可能拥有经过认证的文件或启用了 Reader 的文件。
  3. 阅读'AcroForm'条目; (确保您没有 'XFA' 条目,因为用 Porgy and Bess 的 Fraizer 的话来说:Dat's a complication!)。您基本上想先检查是否有(可选的)'SigFlags' 条目,在这种情况下,非零值表示字段数组中有签名。否则,您需要遍历 'Fields' 数组的每个条目以查找字段字典,其中 'FT'(字段类型)条目设置为 'Sig'(签名),'V'(值)不为空的条目。

使用可以使用文档的交叉引用 table 将您导航到正确的间接对象的 PDF 库应该比暴力搜索文档以获取证书更快且占用资源更少.

这不是最佳解决方案,但它是另一个...您可以检查 "Sigflags" 并在第一个匹配项处停止:

grep -m1 "/Sigflags" ${PDF_FILE}

或在目录中获取此类文件:

grep -r --include=*.pdf -m1 -l "/Sigflags" . > signed_pdfs.txt

grep -r --include=*.pdf -m1 -L "/Sigflags" . > non_signed_pdfs.txt

对于大文件,Grep 可以非常快。您可以 运行 在一定时间内批量处理,然后处理生成的列表(.txt 文件)。

请注意,文件可以在签名后进行增量修改,最后一个版本可能未签名。这就是 "signed".

的实际含义

无论如何,如果文件没有 /Sigflags 字符串,几乎可以肯定它从未被签名。

请注意,一致的读者开始向后阅读(从文件末尾开始),因为有交叉引用 table 说明每个对象在哪里。

我建议你使用peepdf来检查文件的内部结构。它支持在文件上执行它的命令。例如:

 $ peepdf -C "search /SigFlags" signed.pdf 

   [6]

  $ peepdf -C "search /SigFlags" non-signed.pdf 

    Not found!!

但我还没有测试过它的性能。您可以使用它来浏览 PDF 的内部结构并从 PDF v1.7 Reference 中学习。在此处查看带有 PDF 示例的附件。

使用命令行,您可以使用 poppler-utils 包中的 pdfsig 工具检查文件是否具有数字签名(适用于 Ubuntu 20.04)。

pdfsig pdffile.pdf

将生成包含签名和验证数据的详细数据的输出。如果您需要扫描 pdf 文件树并获取已签名 pdf 的列表,您可以使用 bash 命令,例如:

find ./path/to/files -iname '*.pdf'  \
-exec bash -c 'pdfsig "[=11=]";  \
if [[ $? -eq 0 ]]; then  \
echo "[=11=]" >> signed-files.txt; fi' {} \;

您将在本地目录的 signed-files.txt 文件中获得签名文件列表。

我发现这比尝试从 pdf 文件中 grep 一些文本要可靠得多(例如,立陶宛的签名服务生成的 pdf 不包含字符串“SigFlags”,这在以前的答案)。