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

使用iTextSharp的外部签名PDF(签名延迟)-签名无效问题(更改/损坏的文档)

经景辉
2023-03-14

目标是实现一个PDF签名过程,其中服务器根据请求向客户端提供要签名的哈希。然后,客户端使用通过PKCS#11接口从智能卡获得的私钥对给定哈希进行签名。然后,签名被发送回服务器,以便使用iTextSharp 5.5.4附加到PDF文件中。

在Acrobat Reader中查看签名时,我发现错误“自签名应用以来,文档已被更改或损坏”。

下面是我在服务器上计算哈希的方法。

 public byte[] GetHashToSign(byte[] unsignedPdfBytes, out string signatureFieldName, out byte[] tempFile)
    {
        byte[] result = null;
        using (PdfReader reader = new PdfReader(unsignedPdfBytes))
        {
            using (MemoryStream stream = new MemoryStream())
            {
                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ETSI_CADES_DETACHED);

                PdfStamper stamper = PdfStamper.CreateSignature(reader, stream, '\0');
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                signatureFieldName = appearance.FieldName;

                MakeSignature.SignExternalContainer(appearance, external, 30000);
                result = SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
                tempFile = stream.ToArray();

            }
            return result;
        }
    }

客户端对给定的哈希签名后,我将签名哈希嵌入到服务器上的pdf中。下面是我用来嵌入签名散列的方法。

 public void EmbedSignature(byte[] tempPdfBytes, string signatureFieldName, byte[] signedBytes, string signedPdfPath)
    {
        using (PdfReader pdfReader = new PdfReader(tempPdfBytes))
        {
            using (FileStream os = File.OpenWrite(signedPdfPath))
            {
                IExternalSignatureContainer external = new MyExternalSignatureContainer(signedBytes);
                MakeSignature.SignDeferred(pdfReader, signatureFieldName, os, external);
            }
        }
    }

另一方面,当我使用SHA-1时,我没有得到任何错误。Acrobat Reader如下验证签名。但是我必须使用SHA256。

下面是我在服务器上使用SHA-1计算哈希的方法。

    public byte[] GetHashToSign(byte[] unsignedPdfBytes, out string signatureFieldName, out byte[] tempFile)
    {
        byte[] result = null;
        using (PdfReader reader = new PdfReader(unsignedPdfBytes))
        {
            using (MemoryStream stream = new MemoryStream())
            {
                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_SHA1);

                PdfStamper stamper = PdfStamper.CreateSignature(reader, stream, '\0');
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                signatureFieldName = appearance.FieldName;

                MakeSignature.SignExternalContainer(appearance, external, 30000);
                result = SHA1Managed.Create().ComputeHash(appearance.GetRangeStream());
                tempFile = stream.ToArray();

            }
            return result;
        }
    }

编辑:已签名PDF-使用SHA256的签名无效

已签名PDF-具有SHA1的有效签名

我研究了所有相关条目,但没有得到任何处理此问题的结果。我错过了什么或做错了什么?

共有1个答案

从智志
2023-03-14

PDF签名有不同的子类型,对嵌入签名有不同的要求。在远离SHA1使用的交换机中,您更改了类型,因此,不仅必须在任何地方用SHA256替换SHA1,还必须以不同的方式生成签名容器。

您曾经生成子类型adbe的PDF签名。pkcs7。sha1:

external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_SHA1);

此子类型定义为:

文件字节范围的SHA1摘要应使用数据类型的ContentInfo封装在CMS SignedData字段中。该签名数据的摘要应作为正常CMS摘要合并。这个价值观很重要。pkcs7。PDF 2.0不推荐使用子筛选器密钥的sha1。为支持向后兼容性,PDF阅读器应为此键处理此值,但PDF编写器不得使用此值。

(ISO 32000-2第12.8.3.3节"CMS(PKCS#7)签名")

由于此子类型中的SHA1没有替代选项,因此当用SHA256替换SHA1时,必须使用不同的子类型。此外,此子类型已被弃用,因此更有理由进行切换。

您选择切换到子类型ETSI. CADES. disache:

external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ETSI_CADES_DETACHED);

此子类型指定为:

当SubFilter值为ETSI. CADES. disach时,Contents的值应为包含签名的DER编码的CMS SignedData二进制数据对象。签名词典应遵循12.7.5.5“签名字段”中给出的规范,并有以下附加限制/约束:

  • ByteRange应涵盖整个文件,包括签名词典,但不包括内容条目

SignedData对象中用作签名字典Contents键值的属性应遵守以下规则:

a)内容类型:应存在,并应始终具有值"id数据"。

b) 签名时间戳:来自受信任的时间戳服务器的时间戳应作为未签名属性出现。时间戳应在签名创建后立即应用于数字签名,以便时间戳指定尽可能接近文档签名时间的时间。ETSI EN 319 122-1第5.3条中的规则应适用。

c)内容时间戳:可以存在。如果内容时间戳属性存在,则应按照ETSI EN 319 122-1中第5.2.8条中定义的相同方式使用。

d) 应仅存在一个SignerInfo属性。

e) 信息摘要:应存在,并应按照CMS(互联网RFC 5652)中的定义使用。

f) 签名证书或签名证书-v2:应用作ESS签名证书属性中描述的签名属性,或ETSI EN 319 122-1第5.2.2条中定义的ESS签名证书-v2属性中描述的签名属性。签名证书属性的详细信息可在Internet RFC 5035中找到。

g) 签署时间:可能存在。如果存在,则包含UTC时间。如果存在“签名时间”属性,则签名时间不应由签名字典中M项的值表示。

h) 可能存在ETSI EN 319 122-1第5.2.5条中定义的签名人位置。在这种情况下,签名词典中的位置条目不应存在。

i) 不得使用这些属性:反签名、内容引用、内容标识符和内容提示。

j)Signer属性-v2:可以使用,并应遵循ETSI EN 319 122-15.2.6.1条款中给出的定义。

k)此处未明确指出的未签名签名属性可以被忽略。

(ISO 32000-2第12.8.3.4节“PDF中使用的CAdES签名”)

正如您所看到的,ETSI. CADES.独立的子类型比不建议使用的adbe.pkcs7.sha1子类型有更具体的要求。然而,主要的区别是,

  • 那是在阿德贝。pkcs7。sha1签名字节范围的sha1摘要是由签名容器签名的消息,该消息封装在其中

因此,您不仅需要在客户端代码中切换哈希算法,还必须以不同的方式使用从GetHashToSign方法返回的哈希值,不再作为要签名的抽象数据消息,而是已经作为要签名的数据。

在您的示例“带SHA256.pdf的无效签名”中,签名字节范围的散列仍然用作签名的抽象消息,这就是它无效的原因。

 类似资料:
  • 目标是实现一个PDF签名过程,在该过程中,服务器(.NET核心服务)根据请求(Electronic)向客户端提供要签名的散列。然后,客户端使用通过PKCS#11接口从智能卡获得的私钥对给定散列进行签名。然后将签名发送回服务器,以便使用iTextSharp将其附加到PDF文件中。 使用node-webcrypto-p11使用智能卡令牌签名哈希的过程目前非常简单(需要进行大量的尝试和错误)。采用的算法

  • 我正在编写一个服务,其中我用一个空容器预签名pdf文件,从pdf文件中提取一个字节范围的散列,并将其发送到另一个服务,这将允许用户使用移动电话对散列进行签名。我拿回一个证书,我将注入到预签名pdf文件中的签名容器中。 签名本身起作用,数字签名是有效的,但我只需要更改可见签名本身的文本。我认为这是可能的,因为可见签名实际上与证书本身没有任何关系,所以显示来自证书的名称只是一种方便,特别是在多个签名的

  • 我试图对pdf文件进行签名,但在Adobe中打开签名的pdf文件时,遇到“文档自签名后已被更改或损坏”错误。 这个错误不是那么描述性的,我不确定应该在哪里查看,因为代码对我来说似乎很好,但显然不是。。 我使用的代码是: 签名的哈希的Base64格式为(tmp文件sha_前缀): 签名(AMA)的Base64格式为: 有人能帮忙吗?

  • 作为我对客户机/服务器pdf签名研究的一部分,我测试了itext pdf延迟签名示例。不幸的是,我得到的合并空签名pdf和哈希值的pdf ie输出显示无效签名。 下面是我的代码片段 我正在使用pkcss11 usb令牌进行签名

  • 我已经研究了所有类似的问题,但找不到一个应用itextsharp延迟签名的案例。 基本上,我的应用程序使用签名对pdf文档进行签名,该签名是由远程web服务创建的。 我的应用程序向这个web服务发送原始文档的哈希(添加空签名字段后可签名字节的哈希),并接收一个Base64编码的签名文件。 我将此签名嵌入到先前生成的临时pdf文件中,该文件具有空签名字段。 最后,我的签名未被验证,因为Adobe R

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