我想用ITextSharp签署pdf文档并返回ltv pdf启用文件

该方法接收pdf文档作为应该签名的字节数组,要签名的证书和TSA客户端,它将签名文档作为字节数组返回,如果有错误则返回null。 现在它返回签名的pdf文档,但它没有启用LTV。 签名文档必须启用LTV。 如何使文档返回LTV启用? 我会非常感谢任何建议。

public byte[] Sign(byte[] document, X509Certificate2 certificate, ITSAClient tsaClient) { byte[] signedDocument = null; IExternalSignature signature = new X509Certificate2Signature(certificate, "SHA-1"); Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser(); Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(certificate.RawData) }; PdfReader reader = new PdfReader(document); MemoryStream ms = new MemoryStream(); PdfStamper st = PdfStamper.CreateSignature(reader, ms, '\0'); PdfSignatureAppearance sap = st.SignatureAppearance; sap.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED; sap.SignatureCreator = "NAME"; sap.Reason = "REASON"; sap.Contact = "CONTACT"; sap.Location = "LOCATION"; sap.SignDate = DateTime.Now; RectangleF rectangle = new RectangleF(400.98139f, 54.88828f, 530, 84.88828f); sap.Layer2Font = iTextSharp.text.FontFactory.GetFont(BaseFont.TIMES_ROMAN, BaseFont.CP1257, 7f); sap.Layer2Font.Color = iTextSharp.text.BaseColor.RED; sap.Layer2Text = string.Format("Signed for testing: {0}", DateTime.Now.ToString("dd.MM.yyyy.")); sap.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION; sap.SetVisibleSignature(new iTextSharp.text.Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height), 1, null); MakeSignature.SignDetached(sap, signature, chain, null, null, tsaClient, 0, CryptoStandard.CMS); st.Close(); ms.Flush(); signedDocument = ms.ToArray(); ms.Close(); reader.Close(); return signedDocument; } 

通常,您不能指望签名创建步骤返回启用LTV的签名。

Leonard Rosenthol(Adobe的主要PDF大师) 在2013年初的iText邮件列表上评论说,虽然签名容器本身已经包含了启用LTV签名所需的所有信息,但这种情况非常罕见且并非总是可行。

(也有例外,例如,有一个Swisscom签名服务生成签名容器,其中包含启用LTV的集成PDF签名所需的所有额外信息。)

因此,通常您必须在第二步中添加所有缺少的信息。

另一方面,这样的第二步意味着这样的第二遍可能会干扰使用CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED进行签名 – 当前的PDF规范要求即使对于这样的认证级别,如果它们仅包含签名validation信息,则允许增量更新,但是还没有看到Adobe Reader在这种情况下没有抱怨。 因此,您可能必须放宽LTV启用的认证级别。

对于执行第二步的iText 5 / Java和iText 7 / Java助手类,可以在这个答案(iText 5)和这个答案(iText 7)中找到 。

我已将iText 5的Java助手类移植到C#:

 using iTextSharp.text; using iTextSharp.text.error_messages; using iTextSharp.text.pdf; using iTextSharp.text.pdf.security; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Ocsp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Ocsp; using Org.BouncyCastle.X509; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security.Cryptography; using System.Text; [...] class AdobeLtvEnabling { /** * Use this constructor with a {@link PdfStamper} in append mode. Otherwise * the existing signatures will be damaged. */ public AdobeLtvEnabling(PdfStamper pdfStamper) { this.pdfStamper = pdfStamper; } /** * Call this method to have LTV information added to the {@link PdfStamper} * given in the constructor. */ public void enable(IOcspClient ocspClient, ICrlClient crlClient) { AcroFields fields = pdfStamper.AcroFields; bool encrypted = pdfStamper.Reader.IsEncrypted(); List names = fields.GetSignatureNames(); foreach (String name in names) { PdfPKCS7 pdfPKCS7 = fields.VerifySignature(name); PdfDictionary signatureDictionary = fields.GetSignatureDictionary(name); X509Certificate certificate = pdfPKCS7.SigningCertificate; addLtvForChain(certificate, ocspClient, crlClient, getSignatureHashKey(signatureDictionary, encrypted)); } outputDss(); } // // the actual LTV enabling methods // void addLtvForChain(X509Certificate certificate, IOcspClient ocspClient, ICrlClient crlClient, PdfName key) { if (seenCertificates.Contains(certificate)) return; seenCertificates.Add(certificate); ValidationData validationData = new ValidationData(); while (certificate != null) { Console.WriteLine(certificate.SubjectDN); X509Certificate issuer = getIssuerCertificate(certificate); validationData.certs.Add(certificate.GetEncoded()); byte[] ocspResponse = ocspClient.GetEncoded(certificate, issuer, null); if (ocspResponse != null) { Console.WriteLine(" with OCSP response"); validationData.ocsps.Add(ocspResponse); X509Certificate ocspSigner = getOcspSignerCertificate(ocspResponse); if (ocspSigner != null) { Console.WriteLine(" signed by {0}\n", ocspSigner.SubjectDN); } addLtvForChain(ocspSigner, ocspClient, crlClient, getOcspHashKey(ocspResponse)); } else { ICollection crl = crlClient.GetEncoded(certificate, null); if (crl != null && crl.Count > 0) { Console.WriteLine(" with {0} CRLs\n", crl.Count); foreach (byte[] crlBytes in crl) { validationData.crls.Add(crlBytes); addLtvForChain(null, ocspClient, crlClient, getCrlHashKey(crlBytes)); } } } certificate = issuer; } validated[key] = validationData; } void outputDss() { PdfWriter writer = pdfStamper.Writer; PdfReader reader = pdfStamper.Reader; PdfDictionary dss = new PdfDictionary(); PdfDictionary vrim = new PdfDictionary(); PdfArray ocsps = new PdfArray(); PdfArray crls = new PdfArray(); PdfArray certs = new PdfArray(); writer.AddDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL5); writer.AddDeveloperExtension(new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 8)); PdfDictionary catalog = reader.Catalog; pdfStamper.MarkUsed(catalog); foreach (PdfName vkey in validated.Keys) { PdfArray ocsp = new PdfArray(); PdfArray crl = new PdfArray(); PdfArray cert = new PdfArray(); PdfDictionary vri = new PdfDictionary(); foreach (byte[] b in validated[vkey].crls) { PdfStream ps = new PdfStream(b); ps.FlateCompress(); PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference; crl.Add(iref); crls.Add(iref); } foreach (byte[] b in validated[vkey].ocsps) { PdfStream ps = new PdfStream(buildOCSPResponse(b)); ps.FlateCompress(); PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference; ocsp.Add(iref); ocsps.Add(iref); } foreach (byte[] b in validated[vkey].certs) { PdfStream ps = new PdfStream(b); ps.FlateCompress(); PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference; cert.Add(iref); certs.Add(iref); } if (ocsp.Length > 0) vri.Put(PdfName.OCSP, writer.AddToBody(ocsp, false).IndirectReference); if (crl.Length > 0) vri.Put(PdfName.CRL, writer.AddToBody(crl, false).IndirectReference); if (cert.Length > 0) vri.Put(PdfName.CERT, writer.AddToBody(cert, false).IndirectReference); vri.Put(PdfName.TU, new PdfDate()); vrim.Put(vkey, writer.AddToBody(vri, false).IndirectReference); } dss.Put(PdfName.VRI, writer.AddToBody(vrim, false).IndirectReference); if (ocsps.Length > 0) dss.Put(PdfName.OCSPS, writer.AddToBody(ocsps, false).IndirectReference); if (crls.Length > 0) dss.Put(PdfName.CRLS, writer.AddToBody(crls, false).IndirectReference); if (certs.Length > 0) dss.Put(PdfName.CERTS, writer.AddToBody(certs, false).IndirectReference); catalog.Put(PdfName.DSS, writer.AddToBody(dss, false).IndirectReference); } // // VRI signature hash key calculation // static PdfName getCrlHashKey(byte[] crlBytes) { X509Crl crl = new X509Crl(CertificateList.GetInstance(crlBytes)); byte[] signatureBytes = crl.GetSignature(); DerOctetString octetString = new DerOctetString(signatureBytes); byte[] octetBytes = octetString.GetEncoded(); byte[] octetHash = hashBytesSha1(octetBytes); PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash)); return octetName; } static PdfName getOcspHashKey(byte[] basicResponseBytes) { BasicOcspResponse basicResponse = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes)); byte[] signatureBytes = basicResponse.Signature.GetBytes(); DerOctetString octetString = new DerOctetString(signatureBytes); byte[] octetBytes = octetString.GetEncoded(); byte[] octetHash = hashBytesSha1(octetBytes); PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash)); return octetName; } static PdfName getSignatureHashKey(PdfDictionary dic, bool encrypted) { PdfString contents = dic.GetAsString(PdfName.CONTENTS); byte[] bc = contents.GetOriginalBytes(); if (PdfName.ETSI_RFC3161.Equals(PdfReader.GetPdfObject(dic.Get(PdfName.SUBFILTER)))) { using (Asn1InputStream din = new Asn1InputStream(bc)) { Asn1Object pkcs = din.ReadObject(); bc = pkcs.GetEncoded(); } } byte[] bt = hashBytesSha1(bc); return new PdfName(Utilities.ConvertToHex(bt)); } static byte[] hashBytesSha1(byte[] b) { SHA1 sha = new SHA1CryptoServiceProvider(); return sha.ComputeHash(b); } // // OCSP response helpers // static X509Certificate getOcspSignerCertificate(byte[] basicResponseBytes) { BasicOcspResponse borRaw = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes)); BasicOcspResp bor = new BasicOcspResp(borRaw); foreach (X509Certificate x509Certificate in bor.GetCerts()) { if (bor.Verify(x509Certificate.GetPublicKey())) return x509Certificate; } return null; } static byte[] buildOCSPResponse(byte[] BasicOCSPResponse) { DerOctetString doctet = new DerOctetString(BasicOCSPResponse); Asn1EncodableVector v2 = new Asn1EncodableVector(); v2.Add(OcspObjectIdentifiers.PkixOcspBasic); v2.Add(doctet); DerEnumerated den = new DerEnumerated(0); Asn1EncodableVector v3 = new Asn1EncodableVector(); v3.Add(den); v3.Add(new DerTaggedObject(true, 0, new DerSequence(v2))); DerSequence seq = new DerSequence(v3); return seq.GetEncoded(); } // // X509 certificate related helpers // static X509Certificate getIssuerCertificate(X509Certificate certificate) { String url = getCACURL(certificate); if (url != null && url.Length > 0) { HttpWebRequest con = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse response = (HttpWebResponse)con.GetResponse(); if (response.StatusCode != HttpStatusCode.OK) throw new IOException(MessageLocalization.GetComposedMessage("invalid.http.response.1", (int)response.StatusCode)); //Get Response Stream inp = response.GetResponseStream(); byte[] buf = new byte[1024]; MemoryStream bout = new MemoryStream(); while (true) { int n = inp.Read(buf, 0, buf.Length); if (n <= 0) break; bout.Write(buf, 0, n); } inp.Close(); var cert2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(bout.ToArray()); return new X509Certificate(X509CertificateStructure.GetInstance(cert2.GetRawCertData())); } try { certificate.Verify(certificate.GetPublicKey()); return null; } catch (Exception e) { } foreach (X509Certificate candidate in extraCertificates) { try { certificate.Verify(candidate.GetPublicKey()); return candidate; } catch (Exception e) { } } return null; } static String getCACURL(X509Certificate certificate) { try { Asn1Object obj = getExtensionValue(certificate, X509Extensions.AuthorityInfoAccess.Id); if (obj == null) { return null; } Asn1Sequence AccessDescriptions = (Asn1Sequence)obj; for (int i = 0; i < AccessDescriptions.Count; i++) { Asn1Sequence AccessDescription = (Asn1Sequence)AccessDescriptions[i]; if (AccessDescription.Count != 2) { continue; } else { if ((AccessDescription[0] is DerObjectIdentifier) && ((DerObjectIdentifier)AccessDescription[0]).Id.Equals("1.3.6.1.5.5.7.48.2")) { String AccessLocation = getStringFromGeneralName((Asn1Object)AccessDescription[1]); return AccessLocation == null ? "" : AccessLocation; } } } } catch { } return null; } static Asn1Object getExtensionValue(X509Certificate certificate, String oid) { byte[] bytes = certificate.GetExtensionValue(new DerObjectIdentifier(oid)).GetDerEncoded(); if (bytes == null) { return null; } Asn1InputStream aIn = new Asn1InputStream(new MemoryStream(bytes)); Asn1OctetString octs = (Asn1OctetString)aIn.ReadObject(); aIn = new Asn1InputStream(new MemoryStream(octs.GetOctets())); return aIn.ReadObject(); } private static String getStringFromGeneralName(Asn1Object names) { Asn1TaggedObject taggedObject = (Asn1TaggedObject) names; return Encoding.GetEncoding(1252).GetString(Asn1OctetString.GetInstance(taggedObject, false).GetOctets()); } // // inner class // class ValidationData { public IList crls = new List(); public IList ocsps = new List(); public IList certs = new List(); } // // member variables // PdfStamper pdfStamper; ISet seenCertificates = new HashSet(); IDictionary validated = new Dictionary(); public static List extraCertificates = new List(); } 

你通常使用这样的类

 PdfReader reader = new PdfReader(signedDocument); FileStream os = new FileStream(ENABLED_PDF, FileMode.Create); PdfStamper pdfStamper = new PdfStamper(reader, os, (char)0, true); AdobeLtvEnabling adobeLtvEnabling = new AdobeLtvEnabling(pdfStamper); IOcspClient ocsp = new OcspClientBouncyCastle(); ICrlClient crl = new CrlClientOnline(); adobeLtvEnabling.enable(ocsp, crl); pdfStamper.Close(); 

原始的Java类有其局限性

假定使用AIA条目可以构建完整的证书链。

这意味着所讨论的每个(非根)证书都包含用于下载其颁发者证书的URL。

这并非总是如此。 为了解决这个限制,我添加了一个public static List extraCertificates ,如果某些证书中没有颁发者证书URL,可以将其他证书作为颁发者证书候选者进行测试。 在调用enable之前,您必须将这些额外的证书添加为BouncyCastle X509Certificate对象。