代码之家  ›  专栏  ›  技术社区  ›  Ken Janka

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

  •  0
  • Ken Janka  · 技术社区  · 7 年前

    该方法以字节数组的形式接收pdf文档,该字节数组应该被签名,要签名的证书和TSA客户机,并以字节数组的形式返回签名的文档,如果有错误,则返回null。现在它返回已签名的pdf文档,但未启用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;
    }
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   mkl    7 年前

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

    伦纳德·罗森索尔(Adobe的主要PDF专家)评论道 on the iText mailing list in early 2013 虽然签名容器本身可能已经包含启用LTV的签名所需的所有信息,但这是非常罕见的,而且并不总是可能的。

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

    另一方面,这样的第二步意味着这样的第二次通过可能会干扰到与其他人的签名 CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED -当前的PDF规范要求,即使对于这样的认证级别,如果增量更新只包含签名验证信息,也允许增量更新,但我还没有看到adobereader在这种情况下不抱怨。因此,您可能必须放宽LTV启用的认证级别。

    this answer (iText 5) this answer (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<String> 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<byte[]> 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<byte[]> crls = new List<byte[]>();
            public IList<byte[]> ocsps = new List<byte[]>();
            public IList<byte[]> certs = new List<byte[]>();
        }
    
        //
        // member variables
        //
        PdfStamper pdfStamper;
        ISet<X509Certificate> seenCertificates = new HashSet<X509Certificate>();
        IDictionary<PdfName, ValidationData> validated = new Dictionary<PdfName, ValidationData>();
    
        public static List<X509Certificate> extraCertificates = new List<X509Certificate>();
    }
    

    你通常是这样使用这个类的

    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条目构建。

    情况并非总是如此。为了绕过这个限制,我添加了一个 public static List extraCertificates 如果某些证书中没有颁发者证书URL,则可以将其他证书作为颁发者证书候选进行测试。打电话之前 enable X509Certificate 物体。