在 PKCS7 (CMS) 中使用相同的响应 xml 签名对多个位置进行签名

2023-12-10

PDF 文档需要使用国家数字身份进行签名。
国家数字身份WebService提供了签署文档的设施,在我的项目中我已经集成了相同的设施。

请求设计服务给出响应PKCS7(CMS)格式。 我想在多个位置附加相同的响应,因此我在收到来自服务的响应后创建多个空签名容器。

我参考了这篇文章:使用 ITextSharp 和 XML 签名对 PDF 进行签名

但在给定的文章中,我们只存在一个签名位置,但我有多个签名位置。

我在用itext锐库。 使用MakeSignature.SignDeferred在多个位置附加签名的方法,但显示 PDF 无效。

请找到我从 Webservice 收到的以下响应 XML:

<?xml version="1.0" encoding="UTF-8"?>
<EsignResp errCode="NA" errMsg="NA" resCode="259A52453BE95D3A1071193995E062E3EAD796AD" status="1" ts="2019-03-18T14:26:59" txn="UKC:eSign:2998:20190318142602814">
    <UserX509Certificate>--Usercerti in base64--</UserX509Certificate>
    <Signatures>
        <DocSignature error="" id="1" sigHashAlgorithm="SHA256">--Signature in base 64 in PKCS7(CMS)---</DocSignature>
    </Signatures>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
                <DigestValue>MrOfovytOIp/8qlEkgamrcyhGTSGTN5aS1P+08Fbwfk=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>BBexJyk47YaTdoDgXaFRCtJq1Gc3KsZNt48/I8X4TgNJ6gh2NI9Y5Y9Tc7bozrK/QRy1VYPOWYq5r/YdunjMQLmJJicyeqeqe2eD+TJ8oecpjCbmhPnDK2VgaJ2h00sfsfdsflIe/toKwAmV4PTBA1a5wkz77hj+HTkWXMkPEIsBUnBirVpHxe2bYaa7jcIIpWtJmqvcSurKTOeyFRa+AFWfwWHB/EzHJlDmgiMXzrNauxJ4HpphNaRU+bO5JdyzJs/8Zx4i6qwSEybkuprL3GdO9C7zMPiC98CTfO2dfUrbZWy1pSvwEqlVXQIfrkp+m2JRbFgT8EEIGfXUS+AJBPRwhY1Xsww==</SignatureValue>
        <KeyInfo>
            <KeyValue>
                <RSAKeyValue>
                    <Modulus>0o9vohWZ3ztI9ea8D/zUEUBRq6c82BE7sFmr1hNMeuGSJQFf39ceesRtGUzlUYVWXcU23P8sVZ5419CHh7ApFzUXaLD72i/2d5FFI0n3iRlTQec9PEUHyrvOCVDpqBhbnrO/EHBqRluUQJTQUtMu5mhPNFV7IIJMTEAsUhCL9adZXXQK9NeK0foRr29Oq7VdEGfSeLzHIibpQmhNPh89oJXqu0cmbNSW4J4i2GmwHQpmsmHaSQcgh4mgVrykO64pAKXPreAPipDHQM1l/e5hilYlWfLHxhC5OdfdfdsbTCTcydQ218IVulFOFhdQt7xVV61TOmoTC2elhWbDqoLJBVU5mBfQ==</Modulus>
                    <Exponent>AQAB</Exponent>
                </RSAKeyValue>
            </KeyValue>
            <X509Data>
                <X509SubjectName>CN=D-Random detail</X509SubjectName>
                <X509Certificate>--public certificate of provider--- </X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</EsignResp>

EDIT:根据最新的通信,Web 服务为我端提供的任何哈希提供响应。他们不验证它。哈希值是任意 64 个字符的字符串。请让我知道我可以使用它在 PDF 文档上附加 PKCS7 签名的可能方法。

下面的代码用于生成请求:

if (System.IO.File.Exists(tempPdf))
System.IO.File.Delete(tempPdf);

using (PdfReader reader = new PdfReader(pdfReadServerPath))
{
    using (FileStream os = System.IO.File.OpenWrite(tempPdf))
    {
        PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0',null,true);

        PdfSignatureAppearance appearance = stamper.SignatureAppearance;

        appearance.SetVisibleSignature(new Rectangle(15, 15, 100, 100), 1, "sign1");

        appearance.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
         AllPagesSignatureContainer external = new AllPagesSignatureContainer(appearance);

        MakeSignature.SignExternalContainer(appearance, external, 8192);
        Stream data = appearance.GetRangeStream();

       Stream data = appearance.GetRangeStream();
        byte[] hash = ReadFully(data); //Convert stream to byte
        _signatureHash = hash;


    }
}
//create sha256 message digest
using (SHA256.Create())
{
    _signatureHash = SHA256.Create().ComputeHash(_signatureHash);
}
bool check = false;
string hexencodedDigest = null;
//create hex encoded sha256 message digest
hexencodedDigest = new BigInteger(1, _signatureHash).ToString(16);
hexencodedDigest = hexencodedDigest.ToUpper();
if (hexencodedDigest.Length == 64)
{
    **Send this hexencoded hash to webservice**
}

下面的代码用于附加签名:

//DLL Call
eSign2_1_Request_Response req_resp = new eSign2_1_Request_Response();

//// Response XML Digest process
string resp_xml = Request.Form["msg"].ToString();//signature response XML;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(resp_xml);
XmlElement EsignResp = xmlDoc.DocumentElement;
if (EsignResp.Attributes != null && EsignResp.Attributes["status"].Value != "1")
{
    req_resp.WriteTextFileLog("errCode: " + EsignResp.Attributes["errCode"].Value + " & Error Message: " + EsignResp.Attributes["errMsg"].Value, "log", base_folder_path);
}
else
{
    req_resp.WriteTextFileLog(resp_xml, "xml", base_folder_path + "\\" + file_withoutExtn + "_responseXML.txt");
    //-------Continue to generate signed PDF by passing parameter to DLL

    XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signatures");

    string signature = nodeList[0].FirstChild.InnerText;

    string signedPdf = @"D:\POC Hosted\TryNSDL\TryNSDL\wwwroot\TempPath\signedPdf.pdf";
    string tempPdf = @"D:\POC Hosted\TryNSDL\TryNSDL\wwwroot\TempPath\tempPdf.pdf";
    using (PdfReader reader = new PdfReader(tempPdf))
    {

        using (FileStream os = System.IO.File.OpenWrite(signedPdf))
        {
            byte[] encodedSignature = Convert.FromBase64String(signature);

            IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);

            MakeSignature.SignDeferred(reader, "sign1", os, external);
        }
    }
}

Allsignature 容器的代码:

public class AllPagesSignatureContainer : IExternalSignatureContainer
{
    public AllPagesSignatureContainer(PdfSignatureAppearance appearance)
    {
        this.appearance = appearance;

    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
        signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);

        PdfStamper stamper = appearance.Stamper;
        PdfReader reader = stamper.Reader;
        PdfDictionary xobject1 = new PdfDictionary();
        PdfDictionary xobject2 = new PdfDictionary();
        xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
        xobject2.Put(PdfName.AP, xobject1);

        PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
        PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");

        for (int i = 2; i < reader.NumberOfPages+1; i++)
        {
            var signatureField = PdfFormField.CreateSignature(stamper.Writer);

            signatureField.Put(PdfName.T, new PdfString("ClientSignature_" + i.ToString()));
            signatureField.Put(PdfName.V, PRefLiteral);
            signatureField.Put(PdfName.F, new PdfNumber("132"));
            signatureField.SetWidget(new Rectangle(15, 15, 100, 100), null);
            signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);

            signatureField.Put(PdfName.AP, xobject1);
            signatureField.SetPage();
            Console.WriteLine(signatureField);

            stamper.AddAnnotation(signatureField, i);
        }
    }

    public byte[] Sign(Stream data)
    {
       return new byte[0];
    }

    PdfSignatureAppearance appearance;

}

我在创建签名时使用了追加模式,但签名没有出现。 adobe reader 中仅可见空签名:/Fileremoved/

如果我在没有附加模式的情况下尝试同样的操作PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0'); and PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2 * (reader.NumberOfPages - 1)) + " 0 R");那么它工作正常:/Fileremoved/,但它只能用于单一签名者。如果我们再次尝试使用相同的 pdf 进行辞职,则旧签名将变得无效。 (显然因为未使用附加模式。)

我想为了在附加模式下签名工作,需要进行更改PdfLiteral- 我不太了解它的实际工作原理。

签名文件:/Fileremoved/ 输入文件:/文件已删除/


第一次快速浏览您的代码发现了两个主要错误。

哈希两次

您对文档数据进行两次哈希处理(为此使用不同的 API...奇怪!):

        Stream data = appearance.GetRangeStream();

        byte[] hash = DigestAlgorithms.Digest(data, "SHA256");

        [...]

        _signatureHash = hash;// signatureHash;
    }
}

[...]
using (SHA256.Create())
{
    _signatureHash = SHA256.Create().ComputeHash(_signatureHash);
}

这是错误的,这是没有意义的。

注入错误的签名容器

You say

请求 Esign 服务以 PKCS7(CMS) 格式给出响应。

但是,您不是使用结果中的 CMS 签名容器,而是尝试构建自己的 CMS 容器,注入 Esign 响应 CMS 容器,就好像它只是一个签名哈希一样:

XmlNodeList UserX509Certificate = xmlDoc.GetElementsByTagName("UserX509Certificate");
byte[] rawdat = Convert.FromBase64String(UserX509Certificate[0].InnerText);
var chain = new List<Org.BouncyCastle.X509.X509Certificate>
{
    Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(rawdat))
};
var signaturee = new PdfPKCS7(null, chain, "SHA256", false);
_signature = signaturee;

_signature.SetExternalDigest(Convert.FromBase64String(signature), null, "RSA");

byte[] encodedSignature = _signature.GetEncodedPKCS7(_hash, null, null, null, CryptoStandard.CMS);

根据您在 XML 中的评论

    <DocSignature error="" id="1" sigHashAlgorithm="SHA256">--Signature in base 64 in PKCS7(CMS)---</DocSignature>

this DocSignature元素包含 CMS 签名容器。

因此,删除上面的代码段,而是将内容放入DocSignature元素(不要忘记进行 Base64 解码)到byte[] encodedSignature。现在您可以像以前一样将其注入到准备好的签名中:

IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);

MakeSignature.SignDeferred(reader, "sign1", os, external);

解决上述问题后,还有两个问题变得明显:

使用错误的文件模式

您打开要写入的流,如下所示:

using (FileStream os = System.IO.File.OpenWrite(signedPdf))

File.OpenWrite is 记录在 learn.microsoft.com to be

相当于FileStream(String, FileMode, FileAccess, FileShare)文件模式设置为的构造函数重载OpenOrCreate,访问权限设置为Write,并将共享模式设置为None.

文件模式OpenOrCreate依次是记录在案指定

操作系统应该打开一个文件(如果存在);否则,应创建一个新文件。

Thus, 如果给定位置已有文件,该文件仍然存在,您可以开始写入其中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 PKCS7 (CMS) 中使用相同的响应 xml 签名对多个位置进行签名 的相关文章

随机推荐

  • 在 WPF 中将组合框绑定到 XML

    我知道这个问题已经被问死了 但是我已经尝试了很多我找到的建议答案 并且当我在 VS2013 中启动 WPF 时 组合框仍然没有填充 就这样吧 我有一个名为 People xml 的 XML 文档 其格式如下
  • 如何在 HTML 中定义内联内容库以与 Magnific-Popup 一起使用?

    我的页面上有许多画廊 可以通过各自的按钮启动 我喜欢在按钮旁边定义页面中画廊的标记 然后使用隐藏的想法 mfp hide 但是 当我添加时 我无法激活弹出窗口delegate关键字 否则 这是我到目前为止的代码 HTML div class
  • 如何更改 Rails 迁移 t.timestamps 以在 postgres 中使用 `timestamp(0) without timezone`

    我正在尝试找出如何更改本机数据类型t timestamps用于 Rails 迁移 postgres 中的默认类型是timestamp without timezone 我想要的是timestamp 0 without timezone 我想
  • Spring:确保首先初始化特定的 bean

    我有一个库进行 log4j 的运行时设置和配置 没有 log4j properties 或 log4j xml 我已经定义了一个名为 MyLoggerFactory 的 bean 我希望它成为第一个使用 spring 初始化的 bean 我
  • jQuery DOM 操作 - 性能比较?

    我正在学习使用 jQuery 进行 DOM 操作 并希望了解浏览器性能最佳实践 假设我有两个 DOM 元素 div p ol 等 我希望用户只能看到第一个元素 然后只能看到第二个元素 I could 使用替换 remove 第一个元素和 a
  • javascript中删除小数点后的数字

    var randomNumber Math random 3 3 5 randomNumber alert randomNumber 这段代码返回一个数字 例如 4 589729345235789 我需要它返回 4 5 因此需要它删除小数点
  • 多语言站点,子目录作为语言(RewriteRule)

    对于我的网站 我想将我的网址重写为 www xxx com en index php 而不是 www xxx com index php language enwww xxx com nl index php 而不是 www xxx com
  • 今天全日历开始日期

    我正在努力从今天开始制定完整的日历 当我这样做时 它会显示整周 calendar fullCalendar Options calendar fullCalendar gotoDate currentDate 其中 currentDate
  • 内存数据库的概念以及如何查看我的数据是否已填充到 HSQL DB 中?

    我在内存数据库中使用 HSQL 来测试我的应用程序 并使用 SQL Server 作为主数据库 现在在进行测试时 HSQL 数据库将填充与我在 SQL Server 中相同的数据 现在我正在尝试测试特定的数据从数据库检索数据的服务 如果直接
  • sql 从函数或存储过程返回表

    这更多的是一个语法问题 我正在尝试编写一个可以嵌入到查询中的存储过程或函数 例如 select from MyBigProcOrFunction 我正在尝试定义一个表格函数 但我不明白如何执行此操作 因为我构建临时表来计算数据 然后才最终在
  • 选择最近 7 天的最大值

    我通过每小时插入描述这些项目的记录作为 cron 作业来跟踪某些项目 我有一张桌子 steamid int eventid auto increment itemid int value int time unix format 我使用每小
  • AWS部署经常出现故障

    因此 我在 EC2 实例上进行了非常基本的部署 除了几个大问题外 该部署基本上可以正常工作 现在我只是通过 ssh 进入盒子并运行 python m SimpleHTTPServer 80 我在安全组上有一个框 允许端口 80 上的 htt
  • 在 SQL Server Management Studio 中执行 NHibernate 生成的准备好的语句

    配置 NHibernate 以显示执行的 SQL 可以完成预期的任务 但是每当需要将 SQL 字符串复制粘贴到 SQL Server Management Studio 中时 我们就必须对其进行大幅重新排列才能兼容 在我开始开发自己的应用程
  • 与下一行、分组、data.table 进行比较

    我有一个数据框 其中包含每个用户每周的页面浏览量 我想确定每个用户在特定事件发生后他们的观点是否增加 减少或保持不变 我的数据如下所示 Userid week xeventinweek numviews Alice 1 2 5 Alice
  • 如何用StepVerifier验证提供的Mono没有完成?

    With StepVerifier很容易检查是否提供Mono已完成 仅通过expectComplete 中的方法StepVerifier 但是如果需要检查相反的情况该怎么办 我尝试使用这种方法 Test public void neverM
  • 使用 Glide 和 FireBase android 存储和显示图像

    我正在尝试使用 Glide 和 Firebase 上传并显示个人资料图片 上传部分工作成功 但是 如果我尝试从数据库加载该图像 它会显示空白 我的动机是在用户进入活动一次时加载 profile image 他可以点击现有图像并根据自己的意愿
  • 如何在 TeamCity 中构建 Delphi 项目

    我正在尝试在 TeamCity 中构建一个 delphi 项目 但无法让它工作 我正在使用 MSBuild 来构建项目 并且还向构建添加了 BDS 参数 但我不断收到错误 MSB4040 项目中没有目标 您需要注意几件事 首先 需要设置De
  • 运行导出 apk 时出现“java.lang.ClassNotFoundException”错误

    我在 Eclipse 中编写了一个 Android 应用程序 并在手机上使用运行命令 在我的手机上运行成功 但我使用 Android Tools gt Export Signed Application Package 导出我的应用程序 然
  • Go 中独特函数的集合

    我正在尝试在 go 中实现一组功能 上下文是一个事件服务器 我想防止 或至少警告 为一个事件多次添加相同的处理程序 我读过 地图通常用作集合 因为可以轻松检查成员资格 if ok set item ok don t add item els
  • 在 PKCS7 (CMS) 中使用相同的响应 xml 签名对多个位置进行签名

    PDF 文档需要使用国家数字身份进行签名 国家数字身份WebService提供了签署文档的设施 在我的项目中我已经集成了相同的设施 请求设计服务给出响应PKCS7 CMS 格式 我想在多个位置附加相同的响应 因此我在收到来自服务的响应后创建