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

如何解决ECDSA 签名的 PDF 无法使用 iText 7 (C#) 进行签名验证,但使用 Adob​​e 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","service": "C=HU,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.NET 5 (.Net 5)。示例代码对应于 iText 自己的示例代码,它们提供给他们的学习书籍(但更新为 7,因为这些书籍是为 iText 5 编写的)。

我正在添加示例 pdf,以及 this google drive 中签名版本的一些组合。它包含一个示例 pdf,它是无符号和纯的。然后使用 ECDSA 和 RSA 密钥分别对该 pdf 进行签名。然后用相反类型的密钥对它们进行签名。以及他们所有的验证结果。注意:在 json 文件中,integrityAndAuthenticity 为简洁起见仅命名为 valid,但其保存的值是 pkcs7.VerifySignatureIntegrityAndAuthenticity() 的结果。所有签名均由我的应用完成(使用 iText 7)。

编辑#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.
    }
}

然后:

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

多亏@mkl 的回复,它消除了一些关于签名格式的困惑,谢天谢地,微软支持 SignData() 方法中的 TLV 序列格式,所以我不必对签名过程进行逆向工程来实现什么我想要。虽然我只假设这个枚举是答案中描述的 TLV 序列,因为它使用不同的 RFC 或 IEEE 规范来引用它。尽管如此,它解决了我的问题。 (我还添加了一个新的 pdf 到驱动器 sample_signed_ecdsa_Rfc3279DerSequence.pdf 和相应的响应 JSON。)可能默认情况下它使用 DSASignatureFormat.IeeeP1363FixedFieldConcatenation,因为指定该参数不会改变签名有效性,但指定另一个使其在 iText 7 中也有效。

至于互操作性,我不确定如何更改代码以使用 IExternalSignatureContainer。我是这个数字签名的新手,我只关注了他们网站上的 iText 5 书和更新的 iText 7 示例,不幸的是,除了 API 参考之外,我无法找到有关它的示例或文档。>

解决方法

您的 ECDSA 签名中存在一个问题,该问题仅被 Adob​​e Acrobat 忽略,而不会被 iText 7 忽略。

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

  • 作为两个 SEQUENCE 值的 TLV INTEGER

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

    (参见 ANSI X9.62、RFC 5480SEC 1: Elliptic Curve Cryptography,在由两个附加的可选值扩展的 SECG 文档中);

  • 作为两个固定长度整数的连接(见 BSI TR-03111),也就是普通格式。

要使用的格式取决于应用的签名算法。例如:

  • 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) 暗示使用纯格式。

不幸的是,您的 ECDSA CMS 签名容器 SignerInfo 对象的 OID 为 1.2.840.10045.2.1,签名算法应该在该位置;并且该 OID 只是 ECDSA 公钥的 OID,根本不是特定的算法标识符。在不同的验证器中,这有不同的效果:

  • Adobe Acrobat 忽略 OID 作为签名算法 OID 无效,并接受 TLV 和纯格式的签名。
  • iText 7 忽略 OID 作为签名算法 OID 无效,并假设 SHAXXXwithECDSA,即期望 TLV 编码的签名值。
  • eSig DSS 认为由于 OID 无效的签名算法 OID 导致签名被加密破坏。

因此,如果您只需要 Adob​​e Acrobat 和 iText 7 接受您的签名,确保 ECDSA 签名值采用 TLV 格式就足够了。

另一方面,如果您希望您的签名更具互操作性,请将基于 iText 7 的签名代码更改为使用 IExternalSignatureContainer 实现(而不是 IExternalSignature ),您可以在其中构建正确的 CMS 签名容器。请注意,iText 中的 ECDSA 支持是有限的;特别是,您将不得不使用暗示 TLV 格式的签名算法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-