基于iTextSharp实现PKCS7签名多位置追加PDF签章问题排查
问题:iTextSharp多位置PDF签章时,SignDeferred方法导致PDF无效/空签章
我现在需要实现PDF多位置签章功能:通过集成的国家数字身份WebService获取PKCS7(CMS)格式签名,将同一份签名结果追加到PDF的多个签章位置。参考单位置签章方案后,使用iTextSharp的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>
补充说明:WebService仅处理我方提供的任意64字符哈希,不做额外验证。
当前实现代码
生成请求代码
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(); 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); } } }
AllPagesSignatureContainer代码
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; }
问题现象
- 使用append模式(
PdfStamper.CreateSignature的最后一个参数为true)创建签名时,PDF仅显示空签章; - 不使用append模式时,单位置签章正常,但无法添加多个签章,且旧签章会失效。
我推测需要修改PdfLiteral的代码来适配append模式,但不清楚具体该怎么实现,希望能得到解决方案。
内容的提问来源于stack exchange,提问作者Urmi_VV_Developer
相关产品推荐
相关产品推荐

