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

h2o*_*dev 3 pdf digital-signature

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

问题是:

如何添加多个数字签名(批准签名)而不会使前一个无效?

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

以下是增量更新期间的 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://stackoverflow.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://stackoverflow.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
Run Code Online (Sandbox Code Playgroud)

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 字段)。消息“已在认证方允许的情况下对本文档进行了更改”是非常合理的。

未签名的 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-

mkl*_*mkl 5

一般来说

如何添加多个数字签名(批准签名)而不会使前一个无效?

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

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

最后,不要在增量更新中添加任何不允许的更改,参见。这个答案

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

  • 在您的情况下,原始 PDF 结构
  • 在您的情况下,更新的 PDF 结构
  • 在您的情况下,共享 PDF 文件“不成功的 PDF 示例”
  • 在您的情况下,共享 PDF 文件“成功的多重签名没有出现”

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

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

您可以在部分中找到有关此内容的详细信息

  • 如何添加签名可视化
  • 具有可视化功能的签名示例

如何添加签名可视化

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

首先,

AP 字典 (可选;PDF 1.2)一个外观字典,指定如何在页面上直观地呈现注释(参见 12.5.5,“外观流”)。

(ISO 32000-2,表 166 — 所有注释词典通用的条目)

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

一个注解可以定义多达三个单独的外观:

  • 当注释不与用户交互时,应使用正常外观。此外观也用于打印注释。

(ISO 32000-2,第 12.5.5 节 — 外观流)

N 流或字典 (必需)注释的正常外观。

(ISO 32000-2,表 170 — 外观字典中的条目)

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

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

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

具有可视化功能的签名示例

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

让我们从 PDFBox 问题PDFBOX-3198 中查看这个示例文件,打印的很漂亮。

在那里你会发现签名字段和小部件组合对象

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
  >>
>> 
Run Code Online (Sandbox Code Playgroud)

除了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 
Run Code Online (Sandbox Code Playgroud)

这个外观流是一个表单类型 1(参见它的TypeSubtypeFormType条目)的表单 XObject ,它具有自己的资源、对象 84 中的图像 XObject Im1和对象 85 中的字体(参见其资源条目)。

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

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

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

85 0 obj
<<
  /F1 86 0 R
>>
endobj
86 0 obj
<<
  /Type /Font
  /Subtype /Type1
  /BaseFont /Helvetica-Bold
  /Encoding /WinAnsiEncoding
>> 
Run Code Online (Sandbox Code Playgroud)

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

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

参见 这个答案的详细信息。

在您的情况下,原始 PDF 结构

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

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

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

此内容流中显示的图像可能意味着签名可视化。但是,在这种情况下,上述两种更改都将完全错误,签名可视化属于签名小部件的外观 xobject,而不是页面内容。

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

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

此外,交叉引用显然被破坏了。在此处复制和粘贴时按设计?通过错误已经在原始文件中?我不能说。

所以底线是,特别是最后一次增量更新完全被破坏,不能作为关于“插入多个数字批准签名而不使前一个无效”的问题的合理基础,因为无关的基础已经是错误的。

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

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

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

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

    其实在最后的修订,批准签名的修订中,你已经更正了。但是只有在最新的修订版中纠正这一点而不是在第一个签名的修订版中才会导致修订版之间的更改,这可能允许也可能不允许......

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

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

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

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

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

    在对您的问题的评论中,您实际上已经承认了这种变化:

    是的,图像被放置到页面中,因为我不知道如何使用作为签名可视化属于签名小部件的外观 xobject。

    关于这个问题,请查看我上面回答的“如何添加签名可视化”部分。

在您的情况下,共享 PDF 文件“不成功的 PDF 示例”

在您共享的完整 PDF 中,情况类似于上面讨论的更新后的 PDF 结构中的情况。但是,有一些细微的差异,并且由于文件的完整性,其他问题会暴露出来。

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

  2. ToUnicode映射- 您的每个字体都有一个ToUnicode条目,指向一个空流。这是无效的:如果字体中有ToUnicode条目,则其值必须是一个包含将字符代码映射到 Unicode 值的 CMap 文件,并且普遍期望映射是完整的,就实际使用的字符代码而言文件有关。

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

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

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

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

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

    但是,在您的情况下,使用了添加的资源,因此它们绝对是不允许的更改的一部分。

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

此外,要多加注意,不要创建不符合标准的 PDF 结构。

在您的情况下,共享 PDF 文件“成功的多重签名没有出现”

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

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

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

除此之外你

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

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

但这些都是在消除所有实际错误后对代码进行微调的项目。