请求的Esign服务以PKCS7(CMS)
格式提供响应。我想在多个位置追加相同的响应,所以我正在创建多个空签名容器后,我收到来自服务的响应。
我参考了这篇文章:使用ITextSharp和XML签名签署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>
编辑:根据最新通信,Web服务为我端提供的任何散列提供响应。它们不会验证它。散列是任意64个字符串。请让我知道什么是可能的方式,我可以使用这一点追加PKCS7签名在一个PDF文件。
下面是生成请求的代码:
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;
}
我在create signature中使用了追加模式,然后signature就不来了。adobe reader中仅可见空签名:/fileremoved/
第一次快速浏览代码会发现两个主要错误。
对文档数据哈希两次(使用不同的API...奇怪!):
Stream data = appearance.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
[...]
_signatureHash = hash;// signatureHash;
}
}
[...]
using (SHA256.Create())
{
_signatureHash = SHA256.Create().ComputeHash(_signatureHash);
}
这是错误的,这是没有意义的。
请求的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>
IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
MakeSignature.SignDeferred(reader, "sign1", os, external);
在您修复了上面的问题之后,又有两个问题变得显而易见:
打开要写入的流,如下所示:
using (FileStream os = System.IO.File.OpenWrite(signedPdf))
file.openwrite
在docs.microsoft.com上记录为
文件模式openorcreate
依次记录为指定
操作系统应该打开一个存在的文件;否则,应创建一个新文件。
因此,如果给定位置已经有一个文件,则该文件将保留,并且您将开始写入该文件。
但是如果您创建的新文件比旧文件短,您就会遇到一个问题:在新文件结束后,仍然有来自旧的、较长的文件的数据。因此,您的结果是两个文件的大杂烩。
这种情况发生在您共享的示例文件中,您的“signedpdf.pdf”的新内容只有175982字节长,但似乎有一些具有该名称的811986字节长的旧文件。因此,您共享的“signedpdf.pdf”文件长度为811986字节,前175982字节包含您的操作结果,其余数据来自其他文件。
如果您将共享的“signedpdf.pdf”文件削减到它的前175982字节,结果看起来要好得多!
要解决此问题,您应该使用文件模式create
相当于请求如果文件不存在,则使用createNew
;否则,请使用truncate
。
using (FileStream os = new FileStream(signedPdf, FileMode.Create, FileAccess.Write, FileShare.None))
如上所述,如果您将您共享的“signedpdf.pdf”文件削减到它的第一个175982字节,结果看起来要好得多!不幸的是,只是更好,还不是好:
通过查看详细信息,您“身份已过期或尚未有效”的原因就会变得更清楚:
即。PDF声称的签约时间为UTC+1时09:47:59。
即。您的证书在UTC+1时间09:48:40之前有效。
由此可见,声称的签名时间是您的用户证书生效前的半分钟以上!这显然是验证器无法接受的...
显然,您的签名服务会根据需要为您创建一个短期证书,从那时起有效期为半小时。并且开始创建PDF签名的时间不在该间隔内。
我怀疑他们会根据你的要求改变签约服务的设计。因此,您将不得不欺骗一点,并稍微使用一个签名时间在未来。
默认情况下,pdfsignatureappeating
构造函数将签名时间设置为当前时间,即执行此行时:
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
幸运的是,如果您立即使用
appearance.SignDate = [some other date time];
此外,如果该签名服务的反应很慢,或者只是在一些重试之后才作出反应,则软件应该明确检查您从中检索的签名容器中的证书,并将其有效性间隔与您声称的签名时间进行比较。如果声称的签名时间不在该间隔内,则重新开始签名!
现在很明显,您使用的AllPagesSignatureContainer
是为一个非常特殊的用例而设计的,并且仍然需要根据您的用例进行调整。
当不在追加模式下签名时,基本上从这个答案中复制的AllPagesSignatureContainer
实现可以正常工作,但当在追加模式下签名时,它会失败。
起初这是合理的,因为该类必须预测将用于签名值的对象号。这种预测依赖于确切的用例,而打开append模式会显著改变这个用例。因此,我在评论中的建议是
如果需要追加模式,请尝试替换
PdfLiteral PRefLiteral = ...
AllPagesSignatureContainer
中的行
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");
附加模式下的iText使用原始文件的压缩特性,即在您的文件中,只要存储一个允许在对象流中存储的间接对象,它就会创建一个对象流。
对于您的文件,iText为对象流保留了一个对象号,它在AllPagesSignatureContainer
预测签名值对象号和实际生成签名值之间保留了一个对象号。因此,在您的文件中,实际签名值对象数比预测数高1。
因此,为了解决具有交叉引用流的PDF的问题,可以简单地将pdfliteral PRefLiteral=...
行替换为
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages + 1) + " 0 R");
即在最初的预测值上加上1。不幸的是,现在对于带有对照表的PDF的预测是错误的。
更好的修复方法是强制iText在预测签名值对象号之前为交叉引用流PDF的对象流保留一个对象号,然后使用原始的预测代码。一种方法是在预测之前创建并编写一个间接对象,例如如下所示:
stamper.Writer.AddToBody(new PdfNull(), stamper.Writer.PdfIndirectReference, true);
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");
复制AllPagesSignatureContainer
实现的答案已相应更新。
我想验证两个APK文件是否已使用相同的证书签名。 我有整个JavaSDK可用,但想从Java代码使跨平台的原因。 有什么想法吗?
我已经使用iTextSharp Dll实现了数字签名,使用单个签名对PDF文件进行签名,创建空签名字段,并使用签名哈希更新签名字段。现在,我想在PDF的每一页都放置相同的数字签名。这是我的客户要求。 我正在使用以下代码:
我想用PHP实现一个XML数字签名。我正在这个验证器上测试签名的正确性。 我得到了错误的签名值,所以我要一步一步地解释我在做什么,请纠正我做错了什么。 我要签名的XML(无新行): 首先,我规范化XML,然后使用sha256对其进行散列,从而生成正确的摘要值。 第二,创建SignedInfo XML元素并规范化它(无新行): 最后,使用RSA-SHA256对SignedInfo元素进行签名。这是产
我想做多个签名的pdf文件,像在一个工作流。我正在使用下面的代码签署我写的pdf文件,这工作很好。 签署文件 而且上面的代码工作得很好。我的新要求是添加多个签名。是否有任何方法可以重用此代码段。我经历了这,这,这,但没有运气。 除此之外,我尝试的是,创建了空白多个空白签名,并尝试附加签名。但它导致创建损坏的文件。我还尝试使用本链接中提到的方法创建文件。还对PDF文档进行了很好的文档数字签名 nul
我需要计算一些数据签名,使用未封装的pkcs7与sha256和RSA。对于原始内容没有问题,使用: 但是我有另一个用户案例,我没有原始内容,只有它的哈希(sha256)Bouncycastle不支持pkcs7签名的“nonewithRSA”或“RSA”,所以我尝试使用自定义ContentSigner,而没有获得与原始内容版本相同的签名。
我对iTextSharp有意见。我有一个带有表单字段的文档,并且我已经为签名生成了字段。当第一个人在文件上签字时,它就会正常工作。Adobe Reader显示有效签名。当我让第二个人在文档上签名时,Adobe Reader显示签名1现在是“未知签名”,签名无效。Adobe reader显示: 此签名中包含的格式或信息有错误(支持信息:SigDict/Contents非法数据)