0
votes

I want to return an ltv enabled pdf after signing a document. I have posted a similar question before and after suggestions from advanced developers like @mkl, I made some modification to my code yet I can't get the desired result. I've been trying for more than a week and I'm very fustrated. Please, if anyone is kind enough to edit the code or offer suggestions that might help me, I will be very grateful. Thanks very much in advance.

All the methods are located in a static class "SignMyPDF" Here is the method I have use to sign my 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;
        }

Method to make document ltv enabled

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);
        }

These other method are helpers for collecting the crls from a certificate object

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];

And finally, this is the code which calls the sign method is located in the form's load handler. Here's everything in the event. The certificate and document to be signed is stored in my project file. So that's why I'm using the "path" variable.

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();
        }

Here is a link to my certificate and the document I want to sign and make ltv enabled.

Certificate

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

Document

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

Please I need help.

1

1 Answers

0
votes

Using the AdobeLtvEnabling helper class from my answer to your previous, more generic question, I could sign your test document with your certificate and private key and then LTV enable that signature. Two restrictions, though:

  • As the information required for LTV enabling are added in a second pass, I couldn't make it work with CERTIFIED_NO_CHANGES_ALLOWED, I had to switch to CERTIFIED_FORM_FILLING.
  • As your certificate and its issuer certificate don't contain AIA information with URLs for downloading the respective issuer certificate, I had to supply them as extra certificates to check when issuer certificates are sought.
  • I had to trust the "Ascertia Root CA 2" for certification in my Adobe Reader settings (trusting the "Ascertia Public CA 1" would also have worked).

Other than the change in certification level I could use your original signing method from the 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;
}

My test method is this:

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();
}

I downloaded the CA and Root certificates from

as indicated on their web site.

The result in a current Adobe Reader:

enter image description here