【问题标题】:External signing PDF with iTextSharp (SignDeferred) - Invalid Signature Problem (altered/corrupted document)使用 iTextSharp (SignDeferred) 的外部签名 PDF - 无效签名问题(更改/损坏的文档)
【发布时间】:2020-12-16 15:11:39
【问题描述】:

目标是实现一个 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;
        }
    }

编辑:Signed PDF - Invalid signature with SHA256

Signed PDF - valid signature with SHA1

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

【问题讨论】:

  • 请分享一个由您的代码签名的示例 PDF 以分析问题。
  • 一个问题,但是:你说 “然后客户端使用通过 PKCS#11 接口从智能卡获得的私钥对给定的哈希进行签名。” - 有这个代码也被改编为处理 SHA256 哈希而不是 SHA1 哈希?
  • 感谢您的回答,我添加了签名的 pdf。
  • 我使用 SHA 256 算法通过 PKCS#11 接口使用智能卡进行签名。
  • 感谢您的文件。我应该在代码中看到问题,但分析文件最终让它们跃入我的视线。我会写一个答案。

标签: c# pdf itext digital-signature


【解决方案1】:

简而言之

PDF 签名有不同的子类型,它们对要嵌入的签名有不同的要求。在您放弃使用 SHA1 时,您会更改类型,因此,不仅必须在任何地方将 SHA1 替换为 SHA256,还必须以不同的方式生成签名容器。

详细说明

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

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

这个子类型定义为:

文档字节范围的 SHA1 摘要应封装在 ContentInfo 类型为 Data 的 CMS SignedData 字段中。该 SignedData 的摘要应合并为正常的 CMS 摘要。 SubFilter 键的值 adbe.pkcs7.sha1 在 PDF 2.0 中已弃用。为了支持向后兼容,PDF 阅读器应该为这个键处理这个值,但是 PDF 编写器不能使用这个值。

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

由于此子类型中的 SHA1 无法替代使用,因此在将 SHA1 替换为 SHA256 时,您必须使用不同的子类型。此外,此子类型已被弃用,因此有更多的理由进行切换。

您选择切换到子类型 ETSI.CAdES.detached:

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

这个子类型被指定为:

12.8.3.4.2 PAdES 签名的签名字典

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

  • ByteRange 应覆盖整个文件,包括签名字典,但不包括 Contents 条目。
  • 签名字典不应包含 Cert 条目。
  • 可以使用签名字典中 M 条目的值来指示签名时间,也可以使用签名时间属性,但不能同时使用两者。

12.8.3.4.3 PAdES 签名的属性

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

a) 内容类型:应存在且始终具有值“id-data”。

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

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

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

e) 消息摘要:应存在并按照 CMS(Internet RFC 5652)中的定义使用。

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

g) 签名时间:可能存在。如果存在,它包含一个 UTC 时间。如果存在signing-time属性,则签名字典中M条目的值不表示签名时间。

h) 如 ETSI EN 319 122-1 中第 5.2.5 条所定义的签名者位置,可能存在。在这种情况下,签名字典中的 Location 条目将不存在。

i) 不得使用以下属性:会签、内容引用、内容标识符和内容提示。

j) signer-attributes-v2:可以使用并且应遵循 ETSI EN 319 122-1 第 5.2.6.1 条中给出的定义。

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

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

如您所见,ETSI.CAdES.detached 子类型比已弃用的 adbe.pkcs7.sha1 子类型具有更具体的要求。不过,主要区别在于,

  • adbe.pkcs7.sha1中签名字节范围的SHA1摘要是签名容器签名的消息,该消息被封装在其中
  • 在 ETSI.CAdES.detached 中,签名字节范围本身由签名容器直接签名,但不封装在其中。

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

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

【讨论】:

  • 感谢您的详细回答,据我了解,您告诉我在客户端更改代码。这是唯一的问题吗? itextsharp的代码有问题吗?
  • 看起来没问题,特别是以前的版本确实有效,并且您只切换了哈希算法和子过滤器。如果客户端更新后没有立即工作,请尝试子过滤器 adbe.pkcs7.detached。它类似于 ETSI.CAdES.detached,只是对 CMS 签名容器的要求较低。
  • 我还有一个问题。 stackoverflow.com/questions/49511383/… 我还检查了您对这里问题的回答。我也有同样的问题。在您的回答中,您说哈希方法存在问题。 (获取签名字段的哈希)据我了解,我以与提出问题的人相同的方式获得哈希。我的哈希方法没有问题,对吧?这个问题的答案我没看清楚。
  • "stackoverflow.com/questions/49511383/… 我还检查了您对这里问题的回答。我有同样的问题。" - 您正在计算 appearance.GetRangeStream() 的哈希值。那是对的。另一个问题的 OP 计算了整个文件的哈希值。那是错误的。因此,您没有同样的问题;也许您有相同的症状,但原因不同。
猜你喜欢
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 2022-01-01
  • 2022-07-07
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 2021-05-11
相关资源
最近更新 更多