【发布时间】:2017-01-23 06:22:51
【问题描述】:
我有一个用 C 编写的 PDF 生成器,现在我想给它添加数字签名。我从一个最小的 PDF 开始,用JSignPdf 签名,现在我正在尝试让我自己的程序生成一个 Adobe 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