简体   繁体   English

ECDSA 签名的 PDF 无法使用 iText 7 (C#) 进行签名验证,但使用 Adobe Reader DC 成功

[英]ECDSA signed PDF fails signature verification with iText 7 (C#), but succeeds with Adobe Reader DC

I have created code with iText 7 that is able to digitally sign a given PDF with a X509 certificate that uses an ECDSA key pair.我使用 iText 7 创建了代码,该代码能够使用使用 ECDSA 密钥对的 X509 证书对给定的 PDF 进行数字签名。 When I open this signed PDF in Acrobat Reader DC, it correctly reads it, and verifies it to be valid (meaing doc is unmodified since signing, etc etc).当我在 Acrobat Reader DC 中打开这个签名的 PDF 时,它会正确读取它,并验证它是有效的(因为签名等原因,文档没有被修改)。

However, when I try to validate this same document with iText 7, the integrity and authenticity check returns false.但是,当我尝试使用 iText 7 验证同一文档时,完整性和真实性检查返回 false。

Here is a sample code:这是一个示例代码:

// ...
PdfDocument pdfDoc = new(new PdfReader(stream));
SignatureUtil signUtil = new(pdfDoc);
IList<string> names = signUtil.GetSignatureNames();
foreach (string name in names) {
   PdfPKCS7 pkcs7 = signUtil.ReadSignatureData(name);
   bool wholeDocument = signUtil.SignatureCoversWholeDocument(name);
   bool signatureIntegrityAndAuthenticity = pkcs7.VerifySignatureIntegrityAndAuthenticity(); // this returns false, even though Adobe has no problem verifying the signature.
// more code to read values and put them in a json
}
// ...

And a sample output that I extract from the signature:还有我从签名中提取的示例 output:

{
  "$id": "1",
  "signatures": [
    {
      "$id": "2",
      "integrityAndAuthenticity": false, // <---- should be true in my opinion.
      "revisionNumber": 1,
      "coversWholeDocument": true,
      "invisibleSignature": true,
      "filterSubType": "ETSI.CAdES.detached",
      "encryptionAlgorithm": "ECDSA",
      "hashAlgorithm": "SHA512",
      "nameOfSigner": "C=HU, CN=Teszt Elek, GIVENNAME=Elek, L=Budapest, O=Teszt ECC Szervezet, SN=202010260807, SURNAME=Teszt",
      "alternateNameOfSigner": null,
      "signDate": "2021-04-22T12:50:33Z",
      "timestamp": {
        "$id": "3",
        "signDate": "2021-04-22T12:50:33Z",
        "service": "C=HU,L=Budapest,O=Microsec Ltd.,2.5.4.97=VATHU-23584497,CN=Test e-Szigno TSA 2017 01",
        "verified": true,
        "hashAlgorithmOid": "2.16.840.1.101.3.4.2.3"
      },
      "location": " Hungary",
      "reason": "Approval",
      "contactInfo": "",
      "name": "GUID_97e1669d-0fbe-409a-a8fc-8518a1bae460",
      "signatureType": "approval",
      "fillInAllowed": true,
      "annotationsAllowed": true,
      "fieldLocks": []
    }
  ],
  "revisions": 1,
  "valid": false // is an aggregate of all the signatures integrity in the array above
}

I am using the latest iText 7 version as of posting, and my platform is ASP.NET 5 (.Net 5).我使用的是最新的 iText 7 版本,我的平台是 ASP.NET 5 (.Net 5)。 The example code corresponds to iText's own example codes, that they provide for their learing books (but updated to 7, since the books were written for iText 5).示例代码对应于 iText 自己的示例代码,它们为他们的学习书籍提供(但更新为 7,因为这些书籍是为 iText 5 编写的)。

I am adding a sample pdf, and some combinations of signed versions in this google drive .我在这个 google drive中添加了一个示例 pdf,以及签名版本的一些组合。 It contains a sample pdf, that is unsigned and pure.它包含一个样本 pdf,它是无符号且纯的。 That pdf is then signed separately with ECDSA and an RSA key.然后使用 ECDSA 和 RSA 密钥分别对 pdf 进行签名。 Those are then both signed with the opposite type of key.然后,它们都使用相反类型的密钥进行签名。 And all of their validation results.以及他们所有的验证结果。 Note: in the json files integrityAndAuthenticity is just named as valid for brevity, but the value it holds is the result of pkcs7.VerifySignatureIntegrityAndAuthenticity() .注意:在integrityAndAuthenticity文件中,为了简洁起见,integrationAndAuthenticity 被命名为valid ,但它持有的值是pkcs7.VerifySignatureIntegrityAndAuthenticity()的结果。 All signing is done by my app (using iText 7).所有签名均由我的应用程序完成(使用 iText 7)。

Edit #1: I am providing the code that does the signing:编辑#1:我正在提供进行签名的代码:

using System;
using System.Security.Cryptography;
using iText.Signatures;

public class EcdsaSignature : IExternalSignature
{
    private readonly string _encryptionAlgorithm;
    private readonly string _hashAlgorithm;
    private readonly ECDsa _pk;

    public EcdsaSignature(ECDsa pk, string hashAlgorithm)
    {
        _pk = pk;
        _hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigest(hashAlgorithm));
        _encryptionAlgorithm = "ECDSA";
    }

    public virtual string GetEncryptionAlgorithm()
    {
        return _encryptionAlgorithm;
    }

    public virtual string GetHashAlgorithm()
    {
        return _hashAlgorithm;
    }

    public virtual byte[] Sign(byte[] message)
    {
        return _pk.SignData(message, new HashAlgorithmName(_hashAlgorithm), DSASignatureFormat.Rfc3279DerSequence); // <---- I have solved the iText 7 issue by providing this enum to the SignData() method.
    }
}

and then:接着:

using (var key = myCertificate.GetECDsaPrivateKey()) {
   /*PdfSigner*/ signer.SignDetached(new EcdsaSignature(key, DigestAlgorithms.SHA512), chainArray, crlList, ocspClient, tsaClient, 0, subfilter);
}

Thanks to @mkl's response it cleared up some confusions about the signature formats, and thankfully Microsoft supports the TLV sequence format in the SignData() method, so I don't have to reverse engineer the signing process to achieve what I want.感谢@mkl 的回复,它消除了关于签名格式的一些困惑,并且幸运的是微软在SignData()方法中支持 TLV 序列格式,所以我不必对签名过程进行逆向工程来实现我想要的。 Although I only assume this enum is the TLV sequence described in the answer, because it uses different RFCs or IEEE specifications to refer to it.虽然我只假设这个枚举是答案中描述的 TLV 序列,因为它使用不同的 RFC 或 IEEE 规范来引用它。 Nonetheless it solved my question.尽管如此,它解决了我的问题。 (I have also added a new pdf to the drive sample_signed_ecdsa_Rfc3279DerSequence.pdf and a corresponding response JSON .) Probably by default it uses DSASignatureFormat.IeeeP1363FixedFieldConcatenation , because specifying that argument didn't change the signature validity, but specifying the other made it valid in iText 7 as well. (I have also added a new pdf to the drive sample_signed_ecdsa_Rfc3279DerSequence.pdf and a corresponding response JSON .) Probably by default it uses DSASignatureFormat.IeeeP1363FixedFieldConcatenation , because specifying that argument didn't change the signature validity, but specifying the other made it valid in iText 7 也是如此。

Now as for interoperability, I'm not sure how I could change my code to use IExternalSignatureContainer .现在至于互操作性,我不确定如何更改我的代码以使用IExternalSignatureContainer I'm new to this digital signature stuff, I have only followed the iText 5 book and the updated iText 7 examples on their site, and unfortunately I have been unable to find examples or documentation about it, other than the API reference.我是这个数字签名的新手,我只在他们的网站上关注了 iText 5 书和更新的 iText 7 示例,不幸的是,除了 ZDB974238714CA8DE634A7A7C14 参考资料之外,我无法找到有关它的示例或文档。

There is an issue in your ECDSA signatures which is only ignored by Adobe Acrobat but not by iText 7.您的 ECDSA 签名中存在一个问题,该问题仅被 Adobe Acrobat 忽略,而 iText 7 则不会。

There are two major formats to encode an ECDSA signature value:编码 ECDSA 签名值有两种主要格式:

  • as a TLV SEQUENCE of two INTEGER values作为两个INTEGER值的 TLV SEQUENCE

     ECDSA-Sig-Value::= SEQUENCE { r INTEGER, s INTEGER }

    (see ANSI X9.62, RFC 5480 , and SEC 1: Elliptic Curve Cryptography , in the SECG document extended by two additional, optional values); (参见 ANSI X9.62、 RFC 5480SEC 1:椭圆曲线密码学,在 SECG 文档中由两个附加的可选值扩展);

  • as the concatenation of two integers with a fixed length (see BSI TR-03111 ), aka the plain format.作为具有固定长度的两个整数的串联(参见BSI TR-03111 ),也就是普通格式。

The format to use depends on the signature algorithm applied.使用的格式取决于应用的签名算法。 For example:例如:

  • SHA512withECDSA (OID 1.2.840.10045.4.3.4) implies the use of the TLV SEQUENCE format. SHA512withECDSA (OID 1.2.840.10045.4.3.4) 暗示使用 TLV SEQUENCE格式。
  • SHA512withPLAIN-ECDSA (OID 0.4.0.127.0.7.1.1.4.1.5) implies the use of the plain format. SHA512withPLAIN-ECDSA (OID 0.4.0.127.0.7.1.1.4.1.5) 暗示使用纯格式。

Unfortunately, though, your ECDSA CMS signature container SignerInfo objects have an OID 1.2.840.10045.2.1 where the signature algorithm should be;不幸的是,您的 ECDSA CMS 签名容器 SignerInfo 对象的 OID 为 1.2.840.10045.2.1 ,其中签名算法应该是; and that OID is merely the OID for an ECDSA public key, not a specific algorithm identifier at all.并且该 OID 只是 ECDSA 公钥的 OID,根本不是特定的算法标识符。 In different validators this has different effects:在不同的验证器中,这具有不同的效果:

  • Adobe Acrobat ignores that the OID is not valid as signature algorithm OID and accepts signatures either in TLV and in plain format. Adobe Acrobat 忽略 OID 作为签名算法 OID 无效,并接受 TLV 和纯格式的签名。
  • iText 7 ignores that the OID is not valid as signature algorithm OID and assumes SHAXXXwithECDSA, ie expects a TLV encoded signature value. iText 7 忽略 OID 作为签名算法 OID 无效并假设 SHAXXXwithECDSA,即期望 TLV 编码的签名值。
  • eSig DSS considers the signature cryptographically broken due to the OID not valid as signature algorithm OID. eSig DSS 将由于 OID 无效而导致的签名密码破坏视为签名算法 OID。

Thus, if you merely need that your signatures are accepted by Adobe Acrobat and iText 7, it suffices to make sure that the ECDSA signature value is in the TLV format.因此,如果您只需要 Adobe Acrobat 和 iText 7 接受您的签名,那么确保 ECDSA 签名值是 TLV 格式就足够了。

On the other hand, if you want your signatures to be more interoperable, change your iText 7 based signing code to use an IExternalSignatureContainer implementation (not an IExternalSignature one) in which you build a correct CMS signature container.另一方面,如果您希望您的签名更具互操作性,请将基于 iText 7 的签名代码更改为使用IExternalSignatureContainer实现(不是IExternalSignature实现),您可以在其中构建正确的 CMS 签名容器。 Beware, the ECDSA support in iText is limited;请注意,iText 中的 ECDSA 支持是有限的; in particular, you will have to use a signature algorithm implying a TLV format.特别是,您将不得不使用暗示 TLV 格式的签名算法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM