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

ECDSA签名的PDF在iText 7(C#)中签名验证失败,但在Adobe Reader DC中成功

叶德运
2023-03-14

我已经用iText 7创建了代码,它能够用使用ECDSA密钥对的X509证书对给定的PDF进行数字签名。当我在Acrobat Reader DC中打开这个签名的PDF时,它正确地读取它,并验证它是有效的(meaing doc在签名后没有修改,等等)。

但是,当我尝试用iText 7验证同一文档时,完整性和真实性检查返回false。

下面是示例代码:

// ...
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
}
// ...

和从签名中提取的示例输出:

{
  "$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
}
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.
    }
}
using (var key = myCertificate.GetECDsaPrivateKey()) {
   /*PdfSigner*/ signer.SignDetached(new EcdsaSignature(key, DigestAlgorithms.SHA512), chainArray, crlList, ocspClient, tsaClient, 0, subfilter);
}

至于互操作性,我不确定如何更改代码以使用iExternalSignatureContainer。我是一个新的数字签名的东西,我只遵循了iText 5的书和更新的iText 7的例子在他们的网站上,不幸的是,我找不到关于它的例子或文档,除了API参考。

共有1个答案

屠坚壁
2023-03-14

您的ECDSA签名中有一个问题,Adobe Acrobat只会忽略它,而iText 7不会忽略它。

编码ECDSA签名值有两种主要格式:

>

  • 作为两个整数值的TLV序列

    ECDSA-Sig-Value ::= SEQUENCE {
      r  INTEGER,
      s  INTEGER
    }
    
    • SHA512WITHECDSA(OID 1.2.840.10045.4.3.4)暗示使用TLV序列格式。
    • SHA512WITHPLAIN-ECDSA(OID 0.4.0.127.0.7.1.1.1.4.1.5)暗示使用纯格式。

    但是不幸的是,您的ECDSA CMS签名容器SignerInfo对象有一个OID 1.2.840.10045.2.1,签名算法应该在其中;而该OID仅仅是ECDSA公钥的OID,根本不是特定的算法标识符。在不同的验证器中,这有不同的效果:

    • Adobe Acrobat忽略OID作为签名算法OID无效,并接受TLV和纯格式的签名。
    • iText 7忽略OID作为签名算法OID无效,并假定为SHAXXXwithECDSA,即需要TLV编码的签名值。
    • 由于OID作为签名算法OID无效,ESIG DSS认为签名已加密中断。

    因此,如果您只需要Adobe Acrobat和iText 7接受您的签名,那么确保ECDSA签名值为TLV格式就足够了。

    另一方面,如果希望签名具有更好的互操作性,请更改基于iText 7的签名代码,使其使用iExternalSignatureContainer实现(而不是iExternalSignature实现),在该实现中构建正确的CMS签名容器。注意,iText中的ECDSA支持是有限的;特别是,您将必须使用隐含TLV格式的签名算法。

  •  类似资料:
    • 最近我需要在Java中使用RSA对一个字符串进行签名,并在C++中验证签名。 在Java,现在我认为一切都是好的,我创建了public.keystore和private.keysore,可以成功地对数据进行签名和veify。但是当我试图用C++验证它时,它显示签名失败。 这是我的Java代码,在Java,我将数据签名到base64String,并将其保存在我的本地文件中,保存为“sig.dat”,

    • 我找不到为什么Foxit PDF阅读器显示我的签名文件无效。首先,我将空白签名容器插入pdf 我发送到外部Web服务以使用合格的签名进行签名。 接下来,我使用webservice的结果对pdf进行了签名。这是外部签名容器: 这是签名代码: 并且有结果: 找不到我做错了什么。

    • 我想创建一个签名并使用openssl验证它。我想有我的签名的十六进制输出。 这是我的密码 我得到这个错误: 如果我在创建签名的过程中删除了-hex,它就可以工作了。

    • 我需要将一个使用iText5进行PDF签名验证/创建的Java程序移植到iText7。 旧代码显然不能按原样工作,因为iText的大部分内容都经过了重组。 我找到的所有关于如何做到这一点的例子和教程都是针对iText5的。(非常好的)白皮书也是如此。它们依赖于通过方法返回的列表,在该方法上执行所有与签名相关的操作。 在iText7中,不再具有该方法。 有人知道iText7的例子/文档吗?

    • 我有一个包含EC私钥的文件: 我有一个证书,它的公钥对应于私钥: 在 pem 格式中: 以txt格式: 我正在努力完成两件事: < li >使用ecdsa-with-SHA256签名算法使用私钥对字节数组进行签名 < li >使用证书中的公钥验证签名是否正确 我尝试过使用充气城堡图书馆。这是我到目前为止所拥有的。我的断言是失败的。

    • 我正在用C#开发一个执行数字签名验证的webserver,以确保pdf文件没有被修改。我使用了iText和iTextSharp。 和我的C#验证码: 在VerifySignature(name)行中;抛出NullReferenceException! 有趣的是,如果我使用C#代码执行签名,我就可以在java中验证它,因为我添加了这些指令:BouncyCastleProvider provider=