从签名的PDF文件中删除字节订单标记?

时间:2014-10-09 13:59:09

标签: c# pdf itextsharp digital-signature byte-order-mark

我正在使用iTextSharp 5.5.1以数字方式使用分离签名(从第三方机构获得)签署PDF文件。一切似乎工作正常,文件是有效的,例如Adobe Reader报告没有问题,将签名显示为有效等。

问题是Java客户端显然存在这些文件的一些问题 - 文件既不能打开也不能解析。
这些文件在标题中有一个字节顺序标记,似乎会导致该行为(\ x00EF \ x00BB \ x00BF)。

我可以像这样识别BOM:

PdfReader reader = new PdfReader(path);
byte[] metadata = reader.Metadata;
// metadata[0], metadata[1], metadata[2] contain the BOM

如何删除BOM(不丢失签名的有效性),或强制iTextSharp库不将这些字节附加到文件中?

2 个答案:

答案 0 :(得分:6)

首先要做的事情是:一旦签署了PDF,就不应该更改该PDF的任何字节,因为如果你这样做,你就会使签名无效。

第二次观察:字节顺序标记是 PDF标题的一部分(PDF始终以%PDF-1.开头)。在此上下文中,它是XMP元数据处理指令中begin属性的值。我不知道任何Java客户端在文件中的任何位置都有该字节序列的问题。如果他们确实遇到问题,那么客户端就会出现问题,而不是文件。

Byte Order Mark表示存在UTF-8字符。在XMP的上下文中,我们在PDF中包含一个包含明文XML文件的流,该文件可由不具有“PDF感知”的软件使用。例如:

2 0 obj
<</Type/Metadata/Subtype/XML/Length 3492>>stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about=""
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
        xmlns:xmp="http://ns.adobe.com/xap/1.0/"
      dc:format="application/pdf"
      pdf:Keywords="Metadata, iText, PDF"
      pdf:Producer="iText® 5.5.4-SNAPSHOT ©2000-2014 iText Group NV (AGPL-version); modified using iText® 5.5.4-SNAPSHOT ©2000-2014 iText Group NV (AGPL-version)"
      xmp:CreateDate="2014-11-07T16:36:55+01:00"
      xmp:CreatorTool="My program using iText"
      xmp:ModifyDate="2014-11-07T16:36:56+01:00"
      xmp:MetadataDate="2014-11-07T16:36:56+01:00">
      <dc:description>
        <rdf:Alt>
          <rdf:li xml:lang="x-default">This example shows how to add metadata</rdf:li>
        </rdf:Alt>
      </dc:description>
      <dc:creator>
        <rdf:Seq>
          <rdf:li>Bruno Lowagie</rdf:li>
        </rdf:Seq>
      </dc:creator>
      <dc:subject>
        <rdf:Bag>
          <rdf:li>Metadata</rdf:li>
          <rdf:li>iText</rdf:li>
          <rdf:li>PDF</rdf:li>
        </rdf:Bag>
      </dc:subject>
      <dc:title>
        <rdf:Alt>
          <rdf:li xml:lang="x-default">Hello World example</rdf:li>
        </rdf:Alt>
      </dc:title>
    </rdf:Description>
  </rdf:RDF>
</x:xmpmeta>                                                            
<?xpacket end="w"?>
endstream

这种非PDF感知软件将查找序列W5M0MpCehiHzreSzNTczkc9d,这是一个不太可能在数据流中偶然出现的序列。

begin属性用于指示流中的字符使用UTF-8编码。他们在那里因为他们在那里是好的做法,但他们不是强制性的(ISO-16684-1)。

您可以按照您的方式检索元数据(byte[] metadata = reader.Metadata;),删除字节,并使用PdfStamper实例更改流:

 stamper.XmpMetadata = metadata;

更改元数据后,您可以对PDF进行签名。

请注意,您问题的一个方面让我感到惊讶。你写道:

// metadata[0], metadata[1], metadata[2] contain the BOM

非常奇怪的是,XMP元数据的前三个字节包含BOM。假设XMP元数据以<?xpacket开头。如果没有,那么你就是通过删除那些字节来做正确的事。

警告: PDF可以包含不同级别的XMP元数据。现在,您正在研究最常见的一个:文档级元数据。您可能会遇到包含页面级XMP元数据的PDF,在图像中使用XMP等...

答案 1 :(得分:1)

快速的方法:

首先:保存两个未加密的文件。 第二步:在保存文件之前删除元数据0到2

但是有一些注意事项:签名方法是否需要BOM?加密方法是否需要BOM?

在确定是否可以/应该删除BOM之前,您还必须确定添加BOM的阶段。

我将快速搜索我的pdf结构文档,看看我能得到什么,但最简单的方法是(未经验证)将整个事物作为字节数组加载,只需从开始删除xEF xBB xBF的文件,然后进行任何签名/加密。但是他们可能会再次添加它......

我将在周末发布更新:)