无法在C#和iTextSharp中启用pdf文档LTV

时间:2018-09-19 12:57:11

标签: c# itext

我想在签署文档后返回启用了ltv的pdf。在诸如@mkl之类的高级开发人员的建议之前和之后,我都发布了类似的问题,我对代码进行了一些修改,但无法获得所需的结果。我已经尝试了一个多星期,我感到非常沮丧。请,如果有人愿意编辑代码或提供可能对我有帮助的建议,我将不胜感激。非常感谢。

所有方法都位于静态类“ SignMyPDF”中 这是我用来签名pdf的方法

public static 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);

            IOcspClient ocspClient = new OcspClientBouncyCastle();

            ICrlClient crlClient = new CrlClientOnline();
            List<ICrlClient> crlList = new List<ICrlClient>();

            string[] crls = GetCrlDistributionPoints(certificate);

            crlClient = new CrlClientOnline(crls);
            crlList.Add(crlClient);

            MakeSignature.SignDetached(sap, signature, chain, crlList, ocspClient, tsaClient, 0, CryptoStandard.CMS);

            // ADD ltv to document
            AddLtv(signedDocument, @"d:\test.pdf", ocspClient, crlClient, tsaClient);

            st.Close();
            ms.Flush();
            signedDocument = ms.ToArray();
            ms.Close();

            reader.Close();

            return signedDocument;
        }

启用文档ltv的方法

public static void AddLtv(byte[] doc, string dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa)
        {
            PdfReader r = new PdfReader(doc);
            FileStream fos = new FileStream(dest, FileMode.Create);
            PdfStamper stp = PdfStamper.CreateSignature(r, fos, '\0', null, true);
            LtvVerification v = stp.LtvVerification;
            AcroFields fields = stp.AcroFields;
            List<string> names = fields.GetSignatureNames();
            string sigName = names[names.Count - 1];
            PdfPKCS7 pkcs7 = fields.VerifySignature(sigName);
            if (pkcs7.IsTsp)
            {
                v.AddVerification(sigName, ocsp, crl,
                    LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
                    LtvVerification.Level.OCSP_CRL,
                    LtvVerification.CertificateInclusion.NO);
            }
            else
            {
                foreach (string name in names)
                {
                    v.AddVerification(name, ocsp, crl,
                        LtvVerification.CertificateOption.WHOLE_CHAIN,
                        LtvVerification.Level.OCSP_CRL,
                        LtvVerification.CertificateInclusion.NO);
                }
            }
            PdfSignatureAppearance sap = stp.SignatureAppearance;
            LtvTimestamp.Timestamp(sap, tsa, null);
        }

这些其他方法是从证书对象收集crl的助手

public static string[] GetCrlDistributionPoints(this X509Certificate2 certificate)
    {
        X509Extension ext = certificate.Extensions.Cast<X509Extension>().FirstOrDefault(
            e => e.Oid.Value == "2.5.29.31");

        if (ext == null || ext.RawData == null || ext.RawData.Length < 11)
            return EmptyStrings;

        int prev = -2;
        List<string> items = new List<string>();
        while (prev != -1 && ext.RawData.Length > prev + 1)
        {
            int next = IndexOf(ext.RawData, 0x86, prev == -2 ? 8 : prev + 1);
            if (next == -1)
            {
                if (prev >= 0)
                {
                    string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, ext.RawData.Length - (prev + 2));
                    items.Add(item);
                }

                break;
            }

            if (prev >= 0 && next > prev)
            {
                string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, next - (prev + 2));
                items.Add(item);
            }

            prev = next;
        }

        return items.ToArray();
    }

    static int IndexOf(byte[] instance, byte item, int start)
    {
        for (int i = start, l = instance.Length; i < l; i++)
            if (instance[i] == item)
                return i;

        return -1;
    }

    static string[] EmptyStrings = new string[0];

最后,这是调用sign方法的代码位于表单的加载处理程序中。这是活动中的所有内容。要签名的证书和文档存储在我的项目文件中。这就是为什么我使用“ path”变量。

private void Form1_Load(object sender, EventArgs e)
        {
            string path = Application.StartupPath;
            path = path.Substring(0, path.LastIndexOf("\\"));
            path = path.Substring(0, path.LastIndexOf("\\"));

            byte[] document = System.IO.File.ReadAllBytes(path + "\\test.pdf");

            X509Certificate2 certificate = new X509Certificate2(path + "\\cert.pfx", "misko123");

            iTextSharp.text.pdf.security.ITSAClient tsaClient = new iTextSharp.text.pdf.security.TSAClientBouncyCastle("http://timestamp.comodoca.com");

            byte[] signedDocument = SignMyPDF.Sign(document, certificate, tsaClient);


            path = path + "\\test_signed.pdf";
            System.IO.File.WriteAllBytes(path, signedDocument);

            System.Diagnostics.Process.Start(path);
            this.Close();
        }

这里是指向我的证书和我要签名并启用ltv的文档的链接。

证书

https://drive.google.com/file/d/1P82Cc2sFN_z7fYImEvInWIRkD30OzPt6/view?usp=sharing

文档

https://drive.google.com/file/d/10_SqgmBrmzcQ4Motj6eVZkD0vb812HFY/view?usp=sharing

请帮助我。

1 个答案:

答案 0 :(得分:0)

使用从my answeryour previous, more generic questionAdobeLtvEnabling助手类,我可以使用证书和私钥对测试文档进行签名,然后LTV启用该签名。但是有两个限制:

  • 在第二遍添加启用LTV所需的信息后,我无法使其与CERTIFIED_NO_CHANGES_ALLOWED一起使用,因此我不得不切换到CERTIFIED_FORM_FILLING
  • 由于您的证书及其颁发者证书不包含带有用于下载相应颁发者证书的URL的AIA信息,因此我必须提供它们作为额外的证书,以检查何时查找颁发者证书。
  • 我必须信任Adobe Reader设置中用于认证的“ Ascertia根CA 2”(也可以信任“ Ascertia Public CA 1”)。

除了认证级别的更改外,我可以使用previous question中的原始签名方法:

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.CertificationLevel = PdfSignatureAppearance.CERTIFIED_FORM_FILLING;
    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;
}

我的测试方法是:

using System;
using System.Drawing;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using NUnit.Framework;
using Org.BouncyCastle.Asn1.X509;

[...]

[Test]
public void testEnableKenJankasPdf()
{
    byte[] document = File.ReadAllBytes(@"[...]\test.pdf");
    X509Certificate2 certificate = new X509Certificate2(@"[...]\cert.pfx", "misko123");
    X509Certificate2 extra1 = new X509Certificate2(@"[...]\AscertiaPublicCA1.crt");
    X509Certificate2 extra2 = new X509Certificate2(@"[...]\AscertiaRootCA2.crt");

    ITSAClient tsaClient = new TSAClientBouncyCastle("http://timestamp.comodoca.com");

    byte[] signedDocument = Sign(document, certificate, tsaClient);
    File.WriteAllBytes(@"[...]\Test_KenJanka-signed.pdf", signedDocument);

    PdfReader reader = new PdfReader(signedDocument);
    FileStream os = new FileStream(@"[...]\Test_KenJanka-enabled.pdf", FileMode.Create);
    PdfStamper pdfStamper = new PdfStamper(reader, os, (char)0, true);

    AdobeLtvEnabling adobeLtvEnabling = new AdobeLtvEnabling(pdfStamper);
    AdobeLtvEnabling.extraCertificates.Add(new Org.BouncyCastle.X509.X509Certificate(X509CertificateStructure.GetInstance(extra1.GetRawCertData())));
    AdobeLtvEnabling.extraCertificates.Add(new Org.BouncyCastle.X509.X509Certificate(X509CertificateStructure.GetInstance(extra2.GetRawCertData())));
    IOcspClient ocsp = new OcspClientBouncyCastle();
    ICrlClient crl = new CrlClientOnline();
    adobeLtvEnabling.enable(ocsp, crl);

    pdfStamper.Close();
}

我从以下位置下载了CA和根证书

  

如其web site所示。

当前Adobe Reader中的结果:

enter image description here