生成的签名 PDF 使 Adob​​e Reader 崩溃,但没有其他 PDF reader

Generated signed PDF crashes Adobe Reader but no other PDF reader

我们有修改 PDF 的代码,然后对修改后的 PDF 进行数字签名。我们使用 iTextSharp 库 (4.1.6) 的 LGPL 版本对 PDF 进行数字签名。

public static Stream DigitallyCertifyPdfStream(Stream uncertifiedFileStream, CertificationBundle certificationBundle)
{
    using (var memoryStream = new MemoryStream())
    {
        var pdfReader = new PdfReader(uncertifiedFileStream);
        var signatureStamper = PdfStamper.CreateSignature(pdfReader, memoryStream, '[=12=]', null);
        signatureStamper.SetEncryption(null, Encoding.UTF8.GetBytes(certificationBundle.Password), PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_MODIFY_ANNOTATIONS, PdfWriter.STANDARD_ENCRYPTION_128);

        var signatureAppearance = signatureStamper.SignatureAppearance;
        signatureAppearance.Reason = "Approval of design";
        signatureAppearance.Location = "";

        var privateKey = certificationBundle.PrivateKey;
        var signingCertificates = new[] { certificationBundle.Certificate };
        signatureAppearance.SetCrypto(privateKey, signingCertificates, null, PdfSignatureAppearance.WINCER_SIGNED);

        pdfReader.Close();
        signatureStamper.Close();

        return new MemoryStream(memoryStream.ToArray());
    }
}

这是一个展示问题的示例 PDF。 PDF 最初将打开,但随后冻结且不可导航。无论您是否安装了我们的证书来验证此签名,问题似乎都会发生。

这个问题似乎并没有经常发生,而且这个问题只存在于 Adob​​e Reader 中。浏览器 PDF 查看器和 Foxit Reader(进行签名验证)处理得很好。有时会在一段时间后出现一个错误框,上面写着 "There was an error opening the stream."

另外有趣的是,对于经过相同数字签名过程的 PDF,我们在外观完整性报告中观察到以下内容

目前我们不确定这些是否与问题有关。我提到它们是因为它们可能相关。

那么,问题是为什么这个经过数字签名的 PDF 会导致 Adob​​e Reader 崩溃,我们该如何补救?

您的 PDF 包含损坏的图像:

16 0 obj
<</Type/XObject/BitsPerComponent 8/Interpolate true/Width 736/ColorSpace/DeviceRGB/Filter/DCTDecode/Length 0/Height 1242/Subtype/Image>>stream

endstream
endobj 

此 Image XObject 声称包含 RGB 位图图像(736x1242,24 位),同时为空(Length 0)。如果遇到此类缺失数据,PDF 查看器可能会失败(尽管令人印象深刻的是 Adob​​e Reader 锁定了一段时间...)。

请检查源 PDF 中的流是否已损坏。


顺便提一期:

    pdfReader.Close();
    signatureStamper.Close();

您在关闭压模之前关闭 reader。由于压模可能需要在关闭过程中访问 reader,这是一个坏主意。只需切换 Close 调用的顺序。


顺便说一下,您的代码会生成一个 adbe.pkcs7.sha1 签名。这在安全方面是个坏主意,因为无论您在签名中使用哪种安全算法,此机制都将 SHA1 用于第一个文档哈希,否则 SHA1 通常不再被认为是安全的。