我正在使用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库不将这些字节附加到文件中?
答案 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
的文件,然后进行任何签名/加密。但是他们可能会再次添加它......
我将在周末发布更新:)