ECDSA签名的PDF未通过iText 7(C#)的签名验证,但Adobe Reader DC成功验证 [英] ECDSA signed PDF fails signature verification with iText 7 (C#), but succeeds with Adobe Reader DC
问题描述
我已经使用iText 7创建了代码,该代码能够使用使用ECDSA密钥对的X509证书对给定的PDF进行数字签名。当我在Acrobat Reader DC中打开这个签名的PDF时,它正确地读取了它,并验证了它的有效性(意思是文档自签名后未被修改等)。
但是,当我尝试使用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
}
截至发帖,我使用的是最新的iText 7版本,我的平台是ASP.NET5(.NET5)。示例代码对应于iText自己的示例代码,它们为自己的学习书籍提供(但更新为7,因为书籍是为iText 5编写的)。
我在this google drive中添加了一个示例pdf和一些签名版本的组合。它包含一个示例pdf,它是无签名的纯PDF。然后用ECDSA和RSA密钥分别对该PDF进行签名。然后使用相反类型的密钥对这两个密钥进行签名。以及他们所有的验证结果。注:在json文件中,为简洁起见,integrityAndAuthenticity
仅命名为valid
,但它包含的值是pkcs7.VerifySignatureIntegrityAndAuthenticity()
的结果。所有签名都由我的应用程序完成(使用iText 7)。
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);
}
感谢@MKL的回应,它澄清了一些关于签名格式的困惑,而且值得庆幸的是,微软在SignData()
方法中支持TLV序列格式,所以我不必反向设计签名过程来实现我想要的。尽管我只是假设这个枚举是答案中描述的TLV序列,因为它使用不同的RFC或IEEE规范来引用它。尽管如此,它还是解决了我的问题。(我还向驱动器sample_signed_ecdsa_Rfc3279DerSequence.pdf
和相应的响应JSON
添加了一个新的pdf。)可能默认情况下它使用DSASignatureFormat.IeeeP1363FixedFieldConcatenation
,因为指定该参数不会更改签名的有效性,但指定另一个参数会使其在iText 7中也有效。
至于互操作性,我不确定如何才能将代码更改为使用IExternalSignatureContainer
。我是数字签名的新手,我只是在他们的网站上关注了iText 5书籍和更新后的iText 7示例,不幸的是,除了API参考之外,我一直无法找到有关它的示例或文档。
推荐答案
您的ECDSA签名存在问题,只有Adobe Acrobat才会忽略该问题,但iText 7不会忽略该问题。
对ECDSA签名值进行编码有两种主要格式:
作为两个
INTEGER
值的TLVSEQUENCE
ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
(参见SECG文档中的ANSI X9.62,RFC 5480和SEC 1: Elliptic Curve Cryptography,扩展了两个额外的可选值);
为固定长度的两个整数的串联(参见BSI TR-03111),也称为纯格式。
使用的格式取决于应用的签名算法。例如:
- SHA512with ECDSA(OID 1.2.840.10045.4.3.4)暗示使用TLV
SEQUENCE
格式。 - SHA512with PLAIN-ECDSA(OID 0.4.0.127.0.7.1.1.4.1.5)暗示使用普通格式。
- Adobe Acrobat忽略OID作为签名算法OID无效,并接受TLV和纯格式的签名。
- iText 7忽略OID作为签名算法OID无效,并假定SHAXXXwith ECDSA,即需要TLV编码的签名值。
- eSig DSS将签名视为加密破解,因为OID作为签名算法OID无效。
因此,如果您只需要Adobe Acrobat和iText 7接受您的签名,只需确保ECDSA签名值为TLV格式即可。
另一方面,如果您希望签名更具互操作性,请更改基于iText 7的签名代码以使用IExternalSignatureContainer
实现(而不是IExternalSignature
实现),在该实现中您可以构建正确的CMS签名容器。请注意,iText中的ECDSA支持是有限的;尤其是,您必须使用隐含TLV格式的签名算法。
这篇关于ECDSA签名的PDF未通过iText 7(C#)的签名验证,但Adobe Reader DC成功验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!