【问题标题】:Visible signature created using iText 7 not shown in chrome使用 iText 7 创建的可见签名未在 chrome 中显示
【发布时间】:2019-07-30 14:03:17
【问题描述】:

我正在使用 iText 7 签署 pdf 文档。 这没有问题,并且签名显示为有效。

除了数字签名之外,我还想在 pdf 上显示视觉表示。这在数字签名书第 2.4 章创建不同的签名外观中进行了描述。

如果我使用 adobe reader 打开它,生成的 pdf 会显示这种外观。

第一张图片是使用 word 和 另存为 pdf 功能创建的 pdf。 第二张图片是我刚刚随机下载的演示 pdf。

如果我在 chrome 中打开第一个 pdf,则不会显示签名外观文本,但如果我打开最初使用 word 创建的 pdf,则缺少签名外观。

关于没有在 chrome 中显示签名外观的 pdf 有什么问题的任何想法?

编辑:文档链接

编辑 2:代码示例

以下代码示例将使用本地证书对 pdf 文档进行签名,并将一些文本放入未在 chrome 中显示的 SignatureAppearance。

using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace PdfSigning.Lib.Helpers
{
    public class SignPdfTest
    {
        public static byte[] SingPdfUsingCertificate(X509Certificate2 cert2, byte[] pdfToSign)
        {
            var apk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert2.PrivateKey).Private;

            IExternalSignature pks = new PrivateKeySignature(apk, DigestAlgorithms.SHA512);

            var cp = new Org.BouncyCastle.X509.X509CertificateParser();
            var chain = new[] { cp.ReadCertificate(cert2.RawData) };

            using (PdfReader reader = new PdfReader(new MemoryStream(pdfToSign)))
            {
                using (MemoryStream fout = new MemoryStream())
                {
                    StampingProperties sp = new StampingProperties();
                    sp.UseAppendMode();

                    PdfSigner signer = new PdfSigner(reader, fout, sp);
                    PdfSignatureAppearance appearance = signer.GetSignatureAppearance();

                    appearance.SetPageNumber(1);
                    appearance.SetLayer2Text("Hello world");
                    appearance.SetLayer2FontSize(8);

                    Rectangle pr = new Rectangle(10, 10, 200, 100);
                    appearance.SetPageRect(pr);

                    appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION);
                    appearance.SetPageRect(pr);

                    signer.SignDetached(pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
                    return fout.ToArray();
                }
            }
        }
    }
}



    private static void SignDocumentUsingCertificateConfiguration()
    {
        try
        {
            var certificateSignatureConfiguration = new CertificateSignatureConfiguration();
            var cert2 = new X509Certificate2(@"C:\temp\MyCertificate.pfx", "mypassword", X509KeyStorageFlags.Exportable);
            CertificatePdfSigner certPdfSigner = new CertificatePdfSigner(certificateSignatureConfiguration);

            byte[] signedPdf = PdfSigning.Lib.Helpers.SignPdfTest.SingPdfUsingCertificate(cert2, File.ReadAllBytes(@"C:\temp\WordSaveAsPdf.pdf"));

            File.WriteAllBytes(@"C:\temp\WordSaveAsPdf_Signed.pdf", signedPdf);
            Console.WriteLine("Done");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

【问题讨论】:

    标签: c# pdf itext


    【解决方案1】:

    总之

    Chrome 似乎不读取混合参考 PDF 的对象流,尤其是在签名创建期间添加的增量更新中。

    另一方面,iText 在登录期间将其几乎所有更改都放入对象流中。

    因此,Chrome 不知道添加的签名及其外观。

    可以通过强制 iText 不在此处创建对象流来解决这种情况。

    Word 生成的源 PDF 有什么特别之处?

    PDF 文件包含对象交叉引用信息,这些信息将对象编号映射到文件中这些对象的相应起点的偏移量。这些信息可以两种方式存储,作为交叉引用表和(从 PDF 1.5 开始)也作为交叉引用流。此外,从 PDF 1.5 开始,该格式允许将非流对象放入所谓的对象流中,这允许出色的压缩,因为只有流内容可以被压缩。

    由于在引入 PDF 1.5 时大多数 PDF 查看器不支持交叉引用和对象流,因此当时还引入了混合、混合引用样式。在这种风格中,PDF 中显示它所必需的基本对象是正常添加的(不在对象流中),并从交叉引用表中引用。然后将非绝对必要的额外信息添加到对象流中并从交叉引用流中引用。

    MS Word 以这种混合风格创建 PDF,并且几乎是唯一可以这样做的软件。

    iText 签名结果 PDF 有什么特别之处?

    iText 在新的增量更新中将几乎所有更改都放入对象流中。

    但显然,Chrome 并不完全支持对象和交叉引用流,尤其是在结合进一步的增量更新时。

    因此,Chrome 不知道添加的签名及其可视化。

    如何解决问题?

    因此,我们需要做的是说服 iText 在签名期间不要在对象流中添加重要数据。由于成员变量的可见性,这并不像人们想的那么容易;我在这里使用了反射。

    在您的代码中,只需使用以下 PdfSignerNoObjectStream 而不是 PdfSigner

    public class PdfSignerNoObjectStream : PdfSigner
    {
        public PdfSignerNoObjectStream(PdfReader reader, Stream outputStream, StampingProperties properties) : base(reader, outputStream, properties)
        {
        }
    
        protected override PdfDocument InitDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
        {
            try
            {
                return base.InitDocument(reader, writer, properties);
            }
            finally
            {
                FieldInfo propertiesField = typeof(PdfWriter).GetField("properties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                WriterProperties writerProperties = (WriterProperties) propertiesField.GetValue(writer);
                writerProperties.SetFullCompressionMode(false);
            }
        }
    }
    

    不过,请注意,像这样调整 iText 功能并不能保证跨版本工作。我针对最近的 iText-7.1.7-SNAPSHOT 开发状态对其进行了测试;我希望它也适用于以前的 7.1.x 版本。

    这是一个 Chrome 错误吗?还是 iText 错误?还是什么?

    很可能两者兼而有之。

    一方面,Chrome PDF 查看器似乎存在混合参考 PDF 的问题。考虑到它们已经成为 PDF 格式的一部分,这有点令人失望。

    另一方面,PDF 规范要求在混合参考文档的上下文中:

    一般来说,可以隐藏的对象是间接引用指定的可选对象。 [...]

    应该可见的项目包括整个页面树、字体、字体描述符和宽度表。混合引用文件中可能隐藏的对象包括结构树、大纲树、文章线程、注释、目的地、Web Capture 信息和页面标签。

    (ISO 32000-1,第 7.5.8.4 节与不支持压缩参考流的应用程序的兼容性)

    在当前情况下,(更新的)页面对象位于对象流中,即对不支持交叉引用和对象流的查看者隐藏。

    如果底层PdfReader 有任何交叉引用流(HasXrefStm),当前iText 7 PdfDocument 会尝试在PdfWriters 上强制执行FullCompression

    writer.properties.isFullCompression = reader.HasXrefStm();
    

    (PdfDocument 方法Open)

    如果PdfReader 也被识别为混合参考流 (HasHybridXref),则可能不应该强制

    【讨论】:

    • 对此解决方案的注意事项:这适用于使用 Word 创建的 pdf 文档。但是,如果您使用 aspose 单词将 Word 文档转换为 pdf,则签名在 acrobat reader 中显示为无效。对我来说,这意味着必须忍受 chrome 中缺少的视觉外观。
    • 您的意思是您使用 Aspose 将 Word 文件导出为 PDF,然后应用 PdfSignerNoObjectStream,然后出现错误?很可能,PdfSignerNoObjectStream 只能用于混合参考 PDF。仅将其用于流参考 PDF 会损坏它们。你当然可以先检查你有什么样的来源。 PdfReader 有一个 hasHybridXref 方法...或者您可以更新PdfSignerNoObjectStream 以仅将FullCompressionMode 设置为false 如果PdfReader.hasHybridXreftrue...
    【解决方案2】:

    这可能只是由 chrome 内置 PDF 阅读器引起的。据我了解他的情况,在this question 向 Chrome 开发人员请求帮助的人已经收到了一些答案,并被重定向到论坛的另一个部分,在那里他可以获得帮助。我可以尝试使用 itext-sharp 5 重新创建问题(我在以前的项目中使用过),看看 Chrome 中是否没有显示该签名,但可能性不大。

    【讨论】:

    • 您提到的 Chrome 企业版帮助问题似乎与十多年前已弃用的文档内签名有效性标记有关。
    • 好吧,我不喜欢那样的东西,所以我只是尽我所能提供帮助,但似乎没有多大帮助
    【解决方案3】:

    这听起来很像未设置“需要外观”标志的情况。回到我的日子(喘息),iText 表单字段是用尽可能少的图形数据生成的,并将 \NeedsAppearances 标志设置为 true,让有问题的 PDF 查看器(当时 Acrobat Reader 就是这样)它需要生成在尝试将它们绘制到屏幕之前,表单域的外观。

    可见的 PDF 签名保存在表单域中。

    因此,至少从理论上讲,您可以通过告诉 iText(重新?)生成表单字段外观来以编程方式解决此问题。

    【讨论】:

    • 虽然 NeedsAppearances 值通常是缺少或错误外观的可能候选者,但它不是缺少签名可视化的候选者。 PDF 必须提供默认签名外观。 (我们不要考虑那些在签名中带有 n0n1、... 的丑陋层。)
    • 我想通了,但在机会渺茫的情况下将第二种可能性浮出水面并没有什么坏处。听起来你比我更了解 PDF。
    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多