【问题标题】:Signing a PDF with adbe.pkcs7.detached使用 adbe.pkcs7.detached 签署 PDF
【发布时间】:2017-01-23 06:22:51
【问题描述】:

我有一个用 C 编写的 PDF 生成器,现在我想给它添加数字签名。我从一个最小的 PDF 开始,用JSignPdf 签名,现在我正在尝试让我自己的程序生成一个 Adob​​e Acrobat Reader 会以相同方式解释的文件。我已经检查了Digitally sign PDF files 问题,但那里的 cmets 似乎得出结论,应该使用 iText 而不是自己尝试。我不想那样。

更新: 我确实也阅读了 1.7 的 PDF 参考和下面链接的“32000”规范,但有时我对参考的数量有点迷失。从一个工作示例开始通常是我了解所有内容如何组合在一起的最简单方法。很抱歉在我最初的帖子中没有说明这一点。

我已让 Acrobat Reader 确认文件中有签名,但仍有问题。在签名面板中,它显示“由未知签名”,而不是使用密钥中的正确名称。打开“签名属性”时显示“此签名无效,因为此签名中包含的格式或信息存在错误”。在“高级签名属性”中,哈希算法为“不可用”。

根据 Acrobat Reader,来自 JSignPdf 的 PDF 是正确的。在告诉它接受我的自签名证书后,它会为签名显示一个漂亮的绿色复选框。为了找到所需的最少添加,我一个接一个地清除了 PDF 标签,小心地不更改剩余标签的偏移量。这给出了与上面相同的“此签名无效...”错误消息,但仍显示“签名者的身份有效”,并将哈希算法显示为“SHA1”。

问题是造成这种差异的原因是什么,是否有任何工具可以更详细地解释问题所在?

在 /Type/Catalog 字典中,我有一个 /AcroForm。我已经尝试将它放在适当的位置并作为参考,但这没有什么区别。 /AcroForm 包含 /SigFlags 3 和 /Fields [ x 0 R ],其中 x 是带有 /Subtype/Widget 的 /Type/Annot 的 id。 (“endobj”被移到“>>”行以节省一些空间。)

更新: 有一些词典,尽管我现在不记得它们的名字,但其中的“就地”与“参考”很重要。尤其是 1.7 规范中的实现说明有一些这样的,还有一些“规范说这个字段是可选的,但实际上它是必需的”。

2 0 obj <<
 /Type /Catalog
 /Pages 3 0 R
 /AcroForm <<
  /Fields [ 8 0 R ]
  /SigFlags 3
  >>
 >> endobj

在 /Type/Page 对象中,我有 /Annots [ x 0 R ],这似乎是让 Acrobat Reader 接受这里有任何签名所必需的。

更新:有了有效的签名,事情就会发生一些变化。如果没有此引用,Acrobat Reader 确实会说签名是有效的,但不会显示有关它的任何详细信息。有了它,“签名属性”菜单项再次启用。

4 0 obj <<
 /Type /Page
 /Parent 3 0 R
 /Resources <<
  /ProcSet [/PDF /Text]
  /Font << /F1 6 0 R >>
  >>
 /MediaBox [0 0 595 842]
 /Contents 5 0 R
 /Annots [ 8 0 R ]
>> endobj

/Annot 字典包含 /T(Signature1)、/FT/Sig、/Rect[0 0 0 0] 和 /V y 0 R,其中 y 是 /Type/Sig 对象。 JSignPdf 版本还包含“/F 132”和“/P 4 0 R”,但我在PDF Specification 中找不到它们。无论如何,它们似乎不是必需的。

更新:啊,我错过了从第 12.7.1 节到第 12.5.2 节的链接。

8 0 obj <<
 /Subtype/Widget
 /T(Signature1)
 /V 7 0 R
 /Type/Annot
 /FT/Sig
 /Rect [ 0 0 0 0 ]
>> endobj

/Type/Sig 对象包含 /Filter/Adobe.PPKLite、/SubFilter/adbe.pkcs7.detached、/M(D:20160907094326+02'00')、一个 /ByteRange 数组和一个 /Contents 字符串。

更新:我正在使用这种组合,因为它被推荐用于 PDF/A。

7 0 obj <<
 /Contents <3082031f...>
 /Filter/Adobe.PPKLite
 /Type/Sig
 /ByteRange [ 0 904 2907 527 ]
 /SubFilter/adbe.pkcs7.detached
 /M(D:20160907094326+02'00')
>> endobj 

/ByteArray 具有以下值:0、offset-of-last-byte-before-"",以及余数的长度的文件。如果我从 JSignPdf 获取文件,请运行此文件(其中 buf 包含文件数据):

SHA1_Init(ctx);
SHA1_Update(ctx, buf + offset1, len1);
SHA1_Update(ctx, buf + offset2, len2);
SHA1_Final(digest, ctx);

我得到的数据与“:messageDigest”标签的 PKCS7 数据完全相同。我自己的文件也是如此。所以,我相信这些值是正确的。

使用相同的证书和密钥,我得到完全相同的 PKCS7 数据,当然除了 messageDigest 和 rsaEncryption 十六进制转储。但是,将 JSignPdf PKCS7 数据复制到我的文件(因为它们的长度完全相同)不起作用,它仍然抱怨找不到哈希算法。我在 JSignPdf 中的 PKCS7 数据有效,但当然给出了错误的校验和。因此,所有与 OpenSSL 相关的内容很可能都是正确的,问题一定出在 PDF 标记的某个地方。是否有我遗漏的参考资料,或者必须遵循的一些标签或对象顺序?

已解决:此时唯一剩下的就是 ByteRange 标记的值。第一个长度实际上还可以。但是,第二个偏移量在实现中偏离了 1,因为 1 太小了。调整一下,我得到了一个绿色的签名复选框!

【问题讨论】:

  • 我的生成器仍有问题,你能分享一些代码吗?我正在努力放置 PAdES 类型签名。我总是得到一个红色的盒子。
  • Xolido 说“只有部分文件被签名”...

标签: openssl pdf-generation pkcs#7


【解决方案1】:

简而言之,

您可能遇到一次性问题,请参阅答案末尾附近的粗体段落。如果这不是您的问题,请分享相关文件以供分析。

首先,

在尝试操作该格式的文件之前,请考虑阅读该格式的规范。

PDF 规范是 ISO 32000-1(第 2 部分正在构建中),您可以在 Adob​​e 的网站上下载一个免费的副本,只需进行少量更改(明确这不是 ISO 副本):

http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf

(在问题文本的中间,您表明您确实知道该文档存在,但您也表明您没有正确研究它。)

有关集成 PDF 签名的第一个概述,请查看信息安全堆栈交换上的 this answer

详细,

话虽如此,让我们看看您的问题并指出规范的一些适当部分:

在 /Type/Catalog 字典中,我有一个 /AcroForm。我已经尝试将它放在适当的位置并作为参考,但这没有什么区别。 /AcroForm 包含 /SigFlags 3 和 /Fields [ x 0 R ],其中 x 是带有 /Subtype/Widget 的 /Type/Annot 的 id。 (“endobj”被移到“>>”行以节省一些空间。)

文档目录在第 7.7.2 节中指定。

AcroForm 字典被指定为

AcroForm 字典(可选;PDF 1.2) 文档的交互式表单 (AcroForm) 字典(参见 12.7.2,“交互式表单字典")。

特别是它没有指定它是否是直接对象。因此,“这没什么区别”确实。

交互式表单字典在第 12.7.2 节中指定。

特别是,

SigFlags 整数 (可选;PDF 1.3) 一组标志,指定与签名字段相关的各种文档级特征(参见表 219 和 12.7.4.5,“签名字段”)。默认值:0。

...

1 "SignaturesExist" 如果设置,文档包含至少一个签名字段。此标志允许符合标准的阅读器启用与签名处理相关的用户界面项(例如菜单项或按钮),而无需扫描整个文档以查找签名字段的存在。

2 "AppendOnly" 如果设置,则文档包含签名,如果文件以更改其先前内容的方式保存(写入)而不是增量更新,则该签名可能会失效。仅通过将新信息附加到先前版本的末尾来更新文件是安全的(参见 H.7,“更新示例”)。符合要求的读者可以使用此标志通知请求完全保存的用户签名将失效,并需要在继续操作之前明确确认。

Fields 数组 (必需) 对文档根字段(在字段层次结构中没有祖先)的引用数组。

以及描述交互式表单的整个第 12.7 节。

在 /Type/Page 对象中,我有 /Annots [ x 0 R ],这似乎是让 Acrobat Reader 接受这里有任何签名所必需的。

第 12.5 节描述注释。

签名域是一个表单域。表单字段可以在某些页面上具有可视化效果。此类可视化是 Widget 注释。如果一个表单域只有一个widget注解,那么表单域对象和widget对象可以合并为一个对象。

某个页面上的所有注释都是从页面的 Annots 数组中引用的。

但是不,您可以拥有不需要从页面引用的注释的不可见签名(它们确实出现在 Adob​​e Reader 签名面板中,只是不在文档中)。

/Annot 字典包含 /T(Signature1)、/FT/Sig、/Rect[0 0 0 0] 和 /V y 0 R,其中 y 是 /Type/Sig 对象。 JSignPdf 版本还包含“/F 132”和“/P 4 0 R”,但我在 PDF 规范中找不到它们。无论如何,它们似乎不是必需的。

啊,所以你知道规格。请使用它!

TFTV 是表单字段条目,参见。第 12.7.3 节。

RectTypeFP 是注释条目,参见。第 12.5.2 节。

/Type/Sig 对象包含 /Filter/Adobe.PPKLite、/SubFilter/adbe.pkcs7.detached、/M(D:20160907094326+02'00')、一个 /ByteRange 数组和一个 /Contents 字符串。

所有这些条目都在 12.8.1 节中指定,并且在 12.8 的其余部分中进行了更广泛的说明。

/ByteArray 具有以下值:0、offset-of-last-byte-before-"",以及余数的长度文件。

指定为

ByteRange 数组(对于属于签名字段的所有签名以及从权限字典中的 UR3 条目引用的使用权签名都是必需的)应描述摘要计算的确切字节范围的整数(起始字节偏移量,字节长度)。应使用多个不连续的字节范围来描述不包括签名值(theContents 条目)本身的摘要。

..

这个范围应该是整个文件,包括签名字典但不包括签名值本身(Contents条目)。

(尽管这只是一个建议,但通常不接受不遵循此建议的签名。)


你的offset-of-last-byte-before-"好像很奇怪,应该是"

除此之外,您似乎已经正确识别了相关值。因此,如果这不是您的问题的原因,我认为您的 PDF 或您注入的签名容器中还有其他问题。请分享有问题的文件(例如通过公共保管箱或谷歌云端硬盘共享)以供进一步分析。


说了这么多,根据您为您开发签名代码的用例,您可能应该研究 PAdES 样式签名,而不是好的老式签名。

【讨论】:

  • 感谢您确认我已正确理解标签。调整 ByteRange 确实解决了这个问题。我已根据您的 cmets 更新了条目并进行了一些澄清。
【解决方案2】:

证书始终由证书颁发机构认证。因此,证书由权威机构签署。要检查证书,意味着验证此签名,因此您需要授权证书,该证书必须能够在某些根 CA 中处理。 您的签名 pdf 证书是自我认证的。您需要将自签名证书放入证书密钥库中的根 CA 配置单元中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-28
    • 2016-01-17
    • 1970-01-01
    • 2018-02-24
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    相关资源
    最近更新 更多