插入多个数字批准签名而不会使前一个签名无效

Insert multiple digital approval signatures without invalidating the previous one

要签署文档,我们必须将用户签名(转换为 PNG 图像)放在文档上,然后对该 PDF 进行数字签名。根据 PDF 文档,只有第一个需要 "DocMDP" 选项。在我放置第二个签名(批准签名)之前,一切看起来都很好。这会使第一个签名无效,因为文档已更改,不是签名字节中的数据而是由于增量更新(已添加图像)。

问题是:

如何添加多个数字签名(Approval signature)而不会使之前的签名失效?

增量更新时如何处理镜像签名?

下面是增量更新期间的 PDF 结构示例。 (只是一个例子来展示里面的对象。

%PDF-1.7

1 0 obj
<</Type /Pages
/Kids [ 3 0 R]
/Count 1
/MediaBox [0 0 595.28 841.89]
>>
endobj

3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Rotate 0
/Resources 2 0 R
/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>
/Contents [4 0 R 5 0 R] >>
endobj

4 0 obj
<</Length 44>>
stream
BT /F1 24 Tf 175 720 Td (Hello World!)Tj ET
endstream
endobj

5 0 obj
<</Length 93>>
stream
q 15.00 0 0 15.00 80.00 700.00 cm /I1 Do Q
endstream
endobj

2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 7 0 R
>>
/XObject <<
/I1 6 0 R
>>
>>
endobj

7 0 obj
<</Type /Font
/BaseFont /Helvetica
/Subtype /Type1
....
>>
endobj

6 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 3 /BitsPerComponent 8 /Columns 36>>
/SMask 8 0 R
/Length 273>>
stream
[.....]
endstream
endobj

8 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceGray
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns 36>>
/Length 273>>
stream
[.....]
endstream
endobj

5 0 obj
<<
/Producer (Test Producer)
/CreationDate (D:20180708005634)
>>
endobj

9 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj

xref
0 10
0000000000 65535 f
00000000?? 00000 n
0000000??? 00000 n
000000???? 00000 n
000000???? 00000 n
000000???? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
trailer
<<
/Size 10
/Root 9 0 R
/Info 5 0 R 
>>
startxref
123456
%%EOF

3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Rotate 0
/Resources 2 0 R
/Annots [10 0 R]
/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>
/Contents [4 0 R 5 0 R 10 0 R] >>
endobj

10 0 obj
<</Length 93>>
stream
q 15.00 0 0 15.00 180.00 700.00 cm /I2 Do Q
endstream
endobj

11 0 obj
<< /Type /Annot /Subtype /Widget /Rect [180.000000 700.000000 195.000000 780.000000] /P 3 0 R /F 4 /FT /Sig /T (Test Sig #0) /Ff 0 /V 12 0 R >>
endobj

12 0 obj
<< /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange[0 150000 160000 800]                /Contents<12321.....0000000000000> /Reference [ << /Type /SigRef /TransformMethod /DocMDP /TransformParams << /Type /TransformParams /P 2 /V /1.2 >> >> ] /Name (Stack Overflow) /Location (USA) /Reason (Testing Signature 0) /ContactInfo (https://whosebug.com) /M (D:20180708093628+02'00') >>
endobj

13 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 3 /BitsPerComponent 8 /Columns 36>>
/SMask 14 0 R
/Length 273>>
stream
[.....]
endstream
endobj

14 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceGray
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns 36>>
/Length 273>>
stream
[.....]
endstream
endobj


9 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/AcroForm << /Fields [ 11 0 R] /NeedAppearances false /SigFlags 3 >> /Perms << /DocMDP 12 0 R >>
>>
endobj

2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 7 0 R
>>
/XObject <<
/I1 6 0 R /I2 13 0 R
>>
>>
endobj

xref
0 1
0000000000 65535 f
2 2
0000000000 00000 n
0000000??? 00000 n
9 6
000000???? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
trailer
<<
/Size 15
/Root 9 0 R
/Info 5 0 R 
/Prev 123456
>>
startxref
1234567
%%EOF

3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Rotate 0
/Resources 2 0 R
/Annots [11 0 R 16 0 R]
/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>
/Contents [4 0 R 5 0 R 10 0 R 15 0 R] >>
endobj

15 0 obj
<</Length 93>>
stream
q 15.00 0 0 15.00 280.00 700.00 cm /I3 Do Q
endstream
endobj

16 0 obj
<< /Type /Annot /Subtype /Widget /Rect [280.000000 700.000000 195.000000 780.000000] /P 3 0 R /F 4 /FT /Sig /T (Test Sig #1) /Ff 0 /V 17 0 R >>
endobj

17 0 obj
<< /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange[0 150000 160000 800]                /Contents<12321.....0000000000000> /Name (Stack Overflow) /Location (USA) /Reason (Testing Signature 0) /ContactInfo (https://whosebug.com) /M (D:20180708093628+02'00') >>
endobj

18 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 3 /BitsPerComponent 8 /Columns 36>>
/SMask 14 0 R
/Length 273>>
stream
[.....]
endstream
endobj

19 0 obj
<</Type /XObject
/Subtype /Image
/Width 36
/Height 36
/ColorSpace /DeviceGray
/BitsPerComponent 8
/Filter /FlateDecode
/DecodeParms <</Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns 36>>
/Length 273>>
stream
[.....]
endstream
endobj


9 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/AcroForm << /Fields [11 0 R 16 0 R] /SigFlags 1 >>
>>
endobj

2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 7 0 R
>>
/XObject <<
/I1 6 0 R /I2 13 0 R /I3 18 0 R
>>
>>
endobj

xref
0 1
0000000000 65535 f
2 2
0000000000 00000 n
0000000??? 00000 n
9 1
0000000??? 00000 n
15 5
000000???? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
00000????? 00000 n
trailer
<<
/Size 20
/Root 9 0 R
/Info 5 0 R 
/Prev 1234567
>>
startxref
12345678
%%EOF

2018 年 7 月 9 日更新 - 不成功的 PDF 示例

其他 PDF 示例:

原始 PDF

https://drive.google.com/open?id=14_raGyJHHJPv2Ze-pWOJ46SargX0JQ0N

第一个签名 - 认证签名

https://drive.google.com/open?id=12aLqKfTczxRAqB3MjklYNBtg5h8DJJ0b

二次签名-批准签名

https://drive.google.com/open?id=10ghpxuO9gPKRsWcNwsu-ozQH9lth6QVx

带密码的证书"a"

https://drive.google.com/open?id=1eMrjMlVURIVsIo6LLboyii7ewSWoC8xY

这些是我的暂定。如果有人可以使用图像作为签名外观对第一个文件进行两次或更多次数字签名,请分享结果。

2018 年 7 月 11 日更新 - 多次签名成功但未出现

在此试验中,在增量更新期间,我没有克隆任何页面(如前例所示),只是更新了“/Catalog”对象(AcroForm 字段)。消息 "Changes have been made to this document that are permitted by the certifying party" 非常合理。

未签名的 PDF 示例

https://drive.google.com/open?id=1LUQiJMEh73I11NIbL3X8b8LltKseG08a

第一个签名示例

https://drive.google.com/open?id=150H6SYMPpVf5inZy4uWgqSjOuqOk5hoS

第二个签名示例

https://drive.google.com/open?id=1m_6ew4IywNqaOs3uh5o1QLjYKDRDtyNu

第三个签名示例

https://drive.google.com/open?id=1IyZQAAwwyaON35qH1xEw_GSsa2RUBaG-

一般

how to add multiple digital signature (Approval signature) without invalidating the previous one?

首先,显然第一个签名不能是 "no changes allowed" 的认证签名:添加第二个签名是一种更改,因此会使原始签名无效。

此外,您必须在增量更新中添加第二个签名。

最后,不要在增量更新中添加任何不允许的更改,cf。 this answer.

这与您的 PDF 结构和共享 PDF 文件有何关系,您可以在部分

中找到
  • 在你的例子中,原始 PDF 结构
  • 在您的情况下,更新了 PDF 结构
  • 在您的情况下,共享 PDF 文件 "Unsuccessful PDF examples"
  • 在您的情况下,共享 PDF 文件 "Successful multiple signatures without appearance"

How to handle the image signature during incremental update?

这里最后的建议是,不要在增量更新中添加任何不允许的更改,这一点非常重要。特别是:不要向页面内容添加签名可视化,而是使用签名小部件外观流。

您可以在部分

中找到有关此内容的详细信息
  • 如何添加签名可视化
  • 可视化签名示例

如何添加签名可视化

在您的文件中,您已将每个签名字段与其各自的单个小部件注释合并(对象都是 /Type /Annot /Subtype /Widget,即小部件注释,和 /FT /Sig,即签名字段)这是很常见。在这种情况下,不需要从字段进一步引用小部件注释。因此,您只需为组合签名添加外观 field/widget.

首先,

AP dictionary (Optional; PDF 1.2) An appearance dictionary specifying how the annotation shall be presented visually on the page (see 12.5.5, "Appearance streams").

(ISO 32000-2, Table 166 — Entries common to all annotation dictionaries)

因此,您需要将外观字典添加到您的签名字段和小部件对象。

An annotation may define as many as three separate appearances:

  • The normal appearance shall be used when the annotation is not interacting with the user. This appearance is also used for printing the annotation.

(ISO 32000-2, Section 12.5.5 — Appearance streams)

N stream or dictionary (Required) The annotation’s normal appearance.

(ISO 32000-2, Table 170 — Entries in an appearance dictionary)

因此,您的外观字典需要一个带有 /N 键的条目。

在签名小部件的情况下,该值是一个流,它是仅用于 multi-state 注释的流字典,例如复选框表单字段小部件。

这个流是一个 XObject 的形式,cf。 ISO 32000-2 第 8.10.2 节 — 形成字典。

可视化签名示例

在上面的如何添加签名可视化部分中,我引用了 PDF 对象的相关 ISO 32000 部分以用于签名可视化。在您的评论中,您要求提供更多信息,因此我将在此处显示示例的详细信息。

让我们看看 this example file from the PDFBox issue PDFBOX-3198,有点 pretty-printed。

在那里您会找到签名字段和小部件组合对象

77 0 obj
<<
  /FT /Sig
  /Type /Annot
  /Subtype /Widget
  /F 132
  /T (Signature1)
  /V 82 0 R
  /P 13 0 R
  /Rect [0.0 792.0 100.0 842.0]
  /AP <<
    /N 83 0 R
  >>
>> 

除了 AP 条目外,您的签名也使用了其中的大部分条目。这是外观字典,它包含一个带有 N 键的条目。这引用了对象 83:

中签名的 正常 外观
83 0 obj
<<
  /Length 171
  /Type /XObject
  /Subtype /Form
  /Resources <<
    /XObject <<
      /Im1 84 0 R
    >>
    /Font 85 0 R
  >>
  /BBox [0.0 0.0 100.0 50.0]
  /FormType 1
>>
stream
  q
    0.25 0 0 0.25 0 0 cm
    q
      200 0 0 142 0 0 cm
      /Im1 Do
    Q
  Q
  BT
    /F1 10 Tf
    10 35 Td
    15 TL
    (\(Signature line 1\)) Tj
    T*
    (\(Signature line 2\)) Tj
    T*
    (\(Signature line 3\)) Tj
  ET
endstream
endobj 

此外观流是表单类型 1 的表单 XObject(参见其 TypeSubtype FormType 条目)具有自己的资源,对象 84 中的图像 XObject Im1 和对象 85 中的字体(参见其 Resources条目)。

它还有一个边界框(参见其 BBox 条目),它定义了其流中的指令可以绘制外观的区域。 PDF 查看器将在由签名小部件注释的 Rect 条目定义的页面区域中显示该区域,其中包含所有内容,请参见上文。

在流中您会看到绘制位图图像资源的说明和三行文本。

对象85中它的字体资源也没有什么特别之处,要么:

85 0 obj
<<
  /F1 86 0 R
>>
endobj
86 0 obj
<<
  /Type /Font
  /Subtype /Type1
  /BaseFont /Helvetica-Bold
  /Encoding /WinAnsiEncoding
>> 

注意,如果您碰巧需要处理带有页面旋转的文档,请务必

  • 要么不设置签名小部件的NoRotate注释标志(24 F 值的加数)但向表单 XObject 或其内容添加旋转
  • 或设置 NoRotate 注释标志并在计算注释 Rect 值时考虑其影响,

比照。 了解详情。

在你的例子中,原始 PDF 结构

在您的情况下,您使用增量更新并具有允许进行所需更改的认证签名。但除此之外,PDF 完全损坏了。

第一个签名的增量更新中已经有奇怪的地方:

您将内容流(对象 10 0)作为内容流添加到页面。行。但您还在页面的 Annots 数组中引用了此内容流。这没有意义,裸内容流不是注释。

此内容流中显示的图像可能是签名可视化。但是,在那种情况下,上面的两个更改都是完全错误的,签名可视化属于签名小部件的外观对象,不属于页面内容。

第二个签名的增量更新完全怪异,

  • 在资源页面你
    • 将图像 xobject 添加到 xobject 资源(可能已被视为不允许)并且
    • 设置之前已经存在的字体F1指向签名域16 0(这分明是破坏性的废话);
  • 在 AcroForm 字典中你
    • 引用对象15 0作为新字段,但15 0只是一个内容流(破坏性的废话),
    • SigFlags 值从 3 减少到 1(非常不适合签名文档),并且
    • 删除 Perms 条目(文档 MDP 签名不会觉得有趣)。

此外,交叉引用显然已损坏。在此处复制和粘贴时设计的?错误已经在原始文件中?我说不出来。

所以最重要的是,特别是最后一次增量更新完全被破坏了,不能作为关于 "Inserting multiple digital approval signatures without invalidating the previous one" 问题的合理基础,因为不相关的基础知识已经是错误的。

在您的情况下,更新了 PDF 结构

您编辑了 PDF 结构,现在它更有意义了。至少你不会再从资源或字段列表中引用错误的对象,除了一种情况。

这些明显的问题仍然存在:

  1. 如前所述,您仍然引用了一个不合适的对象,在具有认证签名的修订版中,Page 字典仍然引用其 [=185= 中的对象 10 ]Annots数组,但对象10只是一个内容流,没有注释。对象 11,签名字段和小部件,是您应该引用的对象。

    其实在最后的修改,审批签名的修改,你已经改正了。但仅在最新修订版中而不是在第一个签署的修订版中更正此问题会导致修订版之间的更改可能允许也可能不允许...

  2. 您在最终修订版中仍然不恰当地更改 AcroForm 词典内容,您将 SigFlags 值从 3 减少到 1 并删除 Perms 条目。

    前一个更改是个坏主意,因为您向即将到来的 PDF 处理器发出信号,表明他们不需要在增量更新中应用更改,即您请求他们损坏您的签名。

    后一个更改不会立即成为问题,但非常敏感的签名验证器可能会认为这是一个可疑的更改。

  3. 现在您的最终增量更新还会更改页面内容并向其添加绘制图像的新内容流。

    不允许更改签名文档的页面内容,cf. my answer 已在上面的一般部分中引用。因此,即使其他更改不会导致 Adob​​e Reader 声明无效签名,此更改也会。

    在对您的问题的评论中,您实际上已经承认了此更改:

    Yes, the images are placed into the page because I don't know how to use as signature visualization to belong into an appearance xobject of the signature widget.

    关于这个问题请看我上面回答的"How to add signature visualizations"部分。

在您的情况下,共享 PDF 文件 "Unsuccessful PDF examples"

在您共享的完整 PDF 中,情况与上述更新的 PDF 结构 中的情况类似。但是,存在一些细微差别,并且由于文件的完整性,其他问题也暴露出来了。

  1. 未使用的文档信息词典 - 在每次修订中您创建一个新的文档信息词典,但您的预告片一直引用初始修订的词典。 无害 但肯定不是故意的。

  2. Empty ToUnicode maps - 你的每个字体都有一个 ToUnicode 条目指向一个空流。这是无效:如果字体中有ToUnicode条目,它的值必须是stream 包含一个将字符代码映射到 Unicode 值的 CMap 文件,通常的期望是,就文档中实际使用的字符代码而言,映射是完整的。

  3. 重复的字体资源 - 在增量更新中,您向页面资源添加的字体与相同资源中已有的字体具有相同的资源名称,即您为这些名称创建了重复的条目。由于同一词典中的多个条目不得具有相同的键, 这会使您的 PDF 无效

  4. 在应用您的批准签名时,您仍然删除 Perms 条目并参考来自 AcroForm 字典的认证签名。这 可能无害 因为验证器无论如何都会在签名表单字段中找到该签名,但是非常敏感的验证器可能会将其报告为 可疑 还是改吧

  5. 对页面内容的更改 - 就像以前一样(如更新的 PDF 结构 中所讨论)您添加页面内容的签名可视化。和以前一样,第一个签名无害,但它是不允许的更改在 follow-up 个签名中 .

  6. 对页面资源的更改 - 您向页面资源添加新图像 XObjects 和新字体。

    如果它们未在页面内容中使用,则可能无害但是这意味着依靠验证器检查添加资源的使用情况。不检查使用情况的验证器肯定会认为这是 不允许的更改.

    不过,在您的情况下,添加的资源 已被使用 ,因此它们肯定是 不允许的更改的一部分 .

因此,Adobe 完全正确地报告对本文档所做的更改使证书签名 无效,并将该证书签名显示为无效。如果您不想这样做,请停止添加不允许的更改,特别是停止更改页面内容流。而是如上所述将签名可视化放入签名小部件外观流中。

此外,注意一点,不要创建 non-conforming PDF 结构。

在您的情况下,共享 PDF 文件 "Successful multiple signatures without appearance"

通过在没有可视化的情况下签名,您确实摆脱了第 3、5 和 6 项。此外,不再存在未使用的文档信息字典,解决了第 1 项。

您通过不将 Perms 词典添加到带有认证签名的修订目录来删除第 4 项。虽然这当然是可能的,但我建议您还要确保您用于添加更多批准签名的代码不会删除 Perms 字典。

项目 2 仍然存在,您的字体仍然具有空 ToUnicode 值。虽然与手头的问题无关,但这是无效的,并且可能会干扰某些文本提取实现。

除了你

  • DocMDP 转换 P 值从 2 转换为 3;除非你真的需要允许任意注释的创建、删除和修改,否则我建议不要这样做,因为注释可以显着改变文档的外观;

  • 使用带有 PKCS#1 v.1.5 签名方案的 RSA 进行签名;在不需要向后兼容性的新签名应用程序中,我建议使用自 2.1 版以来在 PKCS#1 中定义的 RSASSA-PSS 签名方案,因为对 PKCS#1 v.1.5 方案的接受可能会减少2020年代初期;

但这些是 fine-tune 您的代码一旦所有实际错误都被清除后的项目。

我发现这次谈话非常有趣,我希望我能充分理解它以实现所讨论的想法,就像 h20dev 能够做到的那样。

MKL 对 PDF 和数字签名非常了解,因为我在研究这个主题时阅读了很多他的回复。

对于仍在努力通过增量更新在 PDF 上执行多个数字签名的任何人,请查看此 github 我在执行此任务时取得了巨大成功:

https://github.com/vbuch/node-signpdf