当前位置: 首页 > 知识库问答 >
问题:

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

越俊艾
2023-03-14

要签署文档,我们必须在文档上放置用户签名(转换为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

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

第1个签名示例

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

第二个签名示例

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

第三个签名示例

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

共有2个答案

裴昕
2023-03-14

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

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

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

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

孔冥夜
2023-03-14

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

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

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

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

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

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

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

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

有关详细信息,请参见以下章节

  • 如何添加签名可视化
  • 带有可视化的签名示例

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

首先

AP dictionary(可选;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节-表单字典。

在上面“如何添加签名可视化”一节中,我参考了用于签名可视化的PDF对象的相关ISO 32000章节。在你的评论中,你要求更多,所以我将在这里展示一个例子的细节。

让我们看看这个来自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
  >>
>> 

您的签名也使用了这些条目中的大部分,但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,它有自己的资源、对象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
>> 

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

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

详见本答案。

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

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

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

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

第二个签名的增量更新非常奇怪,

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

    此外,交叉引用显然是错误的。复制时故意

    因此,底线是,尤其是最后一次增量更新完全被打破,不能作为“插入多个数字批准签名而不使前一个签名无效”问题的合理基础,因为不相关的基本原理已经是错误的。

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

    这些显而易见的问题仍然存在:

    >

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

    实际上,在最终修订中,即批准签名的修订中,您已经更正了它。但是,仅在最新修订中而不是在第一个签署的修订中更正这一点会导致修订之间的更改,这可能允许也可能不允许...

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

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

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

    现在,您的最终增量更新也会更改页面内容,并向其中添加新的内容流,从而绘制图像。

    不允许更改已签署文件的页面内容,请参阅上文“概述”部分中我的答案。因此,即使其他更改不会导致Adobe Reader声明无效签名,此更改也会。

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

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

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

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

    >

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

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

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

    对页面内容的更改——就像之前一样(在更新的PDF结构下讨论),您向页面内容添加了一个签名可视化。和以前一样,第一次签名是无害的,但后续签名的更改是不允许的。

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

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

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

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

    此外,要更加注意,不要创建不符合要求的PDF结构。

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

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

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

    除此之外你

    >

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

    使用RSA和PKCS#1 v.1.5签名方案进行签名;在一个没有向后兼容性要求的新签名应用程序中,我建议使用PKCS#1中定义的RSASSA-PSS签名方案,因为PKCS#1 v.1.5方案在20世纪20年代初的接受度可能会下降;

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

 类似资料:
  • 我正在使用iTextSharp&pkcs11RsaSignature在PDF文档的每一页插入数字签名。以下是我的代码: 可以看出,这不是一种非常优雅的编程方式,因为文件的读写次数与页数一样多。是否有其他方法在PDF文件的每一页插入数字签名。 这里实际上要做的是--如果PDF文档被拆分为页(将来),因为内容没有改变,所以技术上数字签名应该对它签署的页有效。但我知道签名会作废的。(重新表述问题--是否

  • 我对iTextSharp有意见。我有一个带有表单字段的文档,并且我已经为签名生成了字段。当第一个人在文件上签字时,它就会正常工作。Adobe Reader显示有效签名。当我让第二个人在文档上签名时,Adobe Reader显示签名1现在是“未知签名”,签名无效。Adobe reader显示: 此签名中包含的格式或信息有错误(支持信息:SigDict/Contents非法数据)

  • 正如Adobe文章“PDF中的数字签名”所述: PDF定义了两种类型的签名:批准和认证。区别如下:批准:文档中可以有任意数量的批准签名。该字段可以选择性地与FieldMDP权限相关联。认证:只能有一个认证签名,并且必须是文档中的第一个。该字段始终与DocMDP相关联。 使用PDFBox示例,我能够成功地将多个签名应用于我的文档:https://github.com/apache/pdfbox/bl

  • 请在关闭之前阅读完整的问题:) 我正在寻找一个有mysqli的准备好的语句(重要的,不是PDO,因为我不能使用它,也不能将一些PDO代码传输到mysqli。),在那里我可以插入一个有很多值(大约2000)的长查询。但是查询必须准备好。 null null null 我的意思是 但当然,我不能把它绑定。

  • 问题内容: 使用JDBC(Oracle),我需要在两个表的每一个中插入大约一千行。像这样: 问题在于两个表都是通过公共序列连接的,因此语句的顺序很重要。 如果我只有一张桌子,那会很容易。在这种情况下,我使用了代码: 但是,这种方法只能用一个准备好的语句,因此只能用一个插入。我该如何解决这个问题? 问题答案: 你可以试试 然后

  • 问题内容: 是否可以使用一个insert语句在一个表中插入多行?我知道如果发生以下情况,将会发生这种情况: 但是,如果我想插入: 只需一个插入命令? 问题答案: 两种解决方案(来源:http : //appsfr.free.fr/spip.php?article21): 或者