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

C# 我想用ITextSharp签署pdf文档并返回启用ltv pdf的文件,c#,pdf,itext,C#,Pdf,Itext,该方法将pdf文档作为应签名的字节数组、要签名的证书和TSA客户端接收,并将签名文档作为字节数组返回,如果有错误,则返回null。现在它返回已签名的pdf文档,但未启用LTV。 已签名的文档必须启用LTV。如何使返回的文档启用LTV?如果有任何建议,我将不胜感激 public byte[] Sign(byte[] document, X509Certificate2 certificate, ITSAClient tsaClient) { byte[] signedDocumen

该方法将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专家)指出,虽然签名容器本身可能已经包含启用LTV的签名所需的所有信息,但这是非常罕见的,而且并不总是可能的

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

因此,一般来说,您必须在第二步中添加所有缺少的信息

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

对于执行第二步的iText 5/Java和iText 7/Java帮助程序类,可以在和中找到

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

原始Java类具有以下限制:

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

这意味着每个有问题的(非根)证书都包含一个用于下载其颁发者证书的URL


情况并非总是如此。为了绕过这个限制,我添加了一个
公共静态列表extraCertificates
,如果某些证书中没有颁发者证书URL,可以将其他证书放入其中,这些证书将作为颁发者证书候选进行测试。在调用
enable
之前,您必须将这些额外的证书添加为BouncyCastle
X509Certificate
对象。

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

Leonard Rosenthol(Adobe的主要PDF专家)指出,虽然签名容器本身可能已经包含启用LTV的签名所需的所有信息,但这是非常罕见的,而且并不总是可能的

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

因此,一般来说,您必须在第二步中添加所有缺少的信息

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

对于执行第二步的iText 5/Java和iText 7/Java帮助程序类,可以在和中找到

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

原始Java类具有以下限制:

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

这意味着每个有问题的(非根)证书都包含一个用于下载其颁发者证书的URL


情况并非总是如此。为了绕过这个限制,我添加了一个
公共静态列表extraCertificates
,如果某些证书中没有颁发者证书URL,可以将其他证书放入其中,这些证书将作为颁发者证书候选进行测试。在调用
enable
之前,您必须将这些额外的证书添加为BouncyCastle
X509Certificate
对象。

重点介绍使用iText 5 for Java启用LTV和使用iText 7 for Java启用LTV。您只需移植适用于您的用例的变体。不便之处,敬请原谅。我使用的是itextShap库。不是我的文字。我对java知之甚少。有人能告诉我如何转换到C#并使用itextSharp在pdf上启用ltv吗?“我使用的是itextShap库,而不是itext。”-itextSharp是从java到C#直至5.5.x版本的itext端口的名称。因此,你就快到了。“请告诉我如何将这个java代码转换成C#”-也许下周我会有一些空闲时间。好的@mkl谢谢你的解释。重点介绍使用iText 5 for Java启用LTV和使用iText 7 for Java启用LTV。您只需移植适用于您的用例的变体。不便之处,敬请原谅。我使用的是itextShap库。不是我的文字。我对java知之甚少。有人能告诉我如何转换到C#并使用itextSharp在pdf上启用ltv吗?“我使用的是itextShap库,而不是itext。”-itextSharp是从java到C#直至5.5.x版本的itext端口的名称。因此,你就快到了。“请告诉我如何将这个java代码转换成C#”-可能下周我会有一些空闲时间。ok@mkl谢谢你的澄清。我尝试了相同的代码,但遇到了问题,在这里发布了问题:,你能检查一下吗,非常感谢。@sonsha该问题的原因是,对于签名中的一个证书,提供了相关的CRL
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();