3
votes

I have successfully signed a pdf with LTV support. I can check the pdf and LTV signature is valid through Adobe Acrobat Reader and also with an external validator.

I'm trying to make the same validation with iTextSharp 5.5.10.

I'm following the iText sample code C5_06

But when I call to ltvVerifier.Verify, I am getting a System.NotSupportedException, The Uri Prefix is not Recognized. I am loading the certificate used to sign the pdf en cert parameter.

Validation code:

   public static bool Validate(byte[] pdfIn, X509Certificate2 cert)
    {
        using (var reader = new PdfReader(pdfIn))
        {
            var fields = reader.AcroFields;
            var signames = fields.GetSignatureNames();

            if (!signames.Any(n => fields.SignatureCoversWholeDocument(n)))
                throw new Exception("None signature covers all document");

            var verifications = signames.Select(n => fields.VerifySignature(n));

            var invalidSignature = verifications.Where(v => !v.Verify());
            var invalidTimeStamp = verifications.Where(v => !v.VerifyTimestampImprint());

            if (invalidSignature.Any())
                throw new Exception("Invalid signature found");
        }

        using (var reader = new PdfReader(pdfIn))
        {
            var ltvVerifier = new LtvVerifier(reader)
            {
                OnlineCheckingAllowed = false,
                CertificateOption = LtvVerification.CertificateOption.WHOLE_CHAIN,
                Certificates = GetChain(cert).ToList(),
                VerifyRootCertificate = false,
                Verifier = new MyVerifier(null)
            };

            var ltvResult = new List<VerificationOK> { };
            ltvVerifier.Verify(ltvResult);

            if (!ltvResult.Any())
                throw new Exception("Ltv verification failed");
        }
        return true;
   }

Auxiliary function that builds a List of X509Certificates from the certificate chain:

    private static X509.X509Certificate[] GetChain(X509Certificate2 myCert)
    {
        var x509Chain = new X509Chain();
        x509Chain.Build(myCert);

        var chain = new List<X509.X509Certificate>();
        foreach(var cert in x509Chain.ChainElements)
        {
            chain.Add(
                DotNetUtilities.FromX509Certificate(cert.Certificate)
                );
        }

        return chain.ToArray();
    }

A custom verifier, just copied from sample:

 class MyVerifier : CertificateVerifier
{
    public MyVerifier(CertificateVerifier verifier) : base(verifier) { }

    override public List<VerificationOK> Verify(
        X509.X509Certificate signCert, X509.X509Certificate issuerCert, DateTime signDate)
    {
        Console.WriteLine(signCert.SubjectDN + ": ALL VERIFICATIONS DONE");
        return new List<VerificationOK>();
    }
}

And this is the relevant stack trace:

  in System.Net.WebRequest.Create(Uri requestUri, Boolean useUriBase)
   in System.Net.WebRequest.Create(String requestUriString)
   in iTextSharp.text.pdf.security.CrlVerifier.GetCrl(X509Certificate signCert, X509Certificate issuerCert)
   in iTextSharp.text.pdf.security.CrlVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
   in iTextSharp.text.pdf.security.OcspVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
   in iTextSharp.text.pdf.security.LtvVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime sigDate)
   in iTextSharp.text.pdf.security.LtvVerifier.VerifySignature()
   in iTextSharp.text.pdf.security.LtvVerifier.Verify(List`1 result)

Thanks.

1
Can you share the PDF in question? It looks like the location of the CRL for some certificate is not given as an URL which System.Net.WebRequest supports.mkl
@mkl, please find the PDF in the following link: drive.google.com/file/d/0BxGvNZY_rGbVRlV5blZFOV9jZ2c/…jruizaranguren
Validate cannot be compiled - "not all code paths return a value". Actually none does.mkl
Which X509Certificate2 do you feed into Validate?mkl
From the PDF signature, the certificate contains this CRLDistributionPoints extension:<br/> X509v3 CRL Distribution Points: <br/> Full Name:<br/> URI:ldap://ldapcomp.cert.fnmt.es/CN=CRL1,OU=AC%20Componentes%20Informaticos,O=FNMT-RCM,C=ES?certificateRevocationList;binary?base?objectclass=cRLDistributionPoint<br/> URI:http://www.cert.fnmt.es/crlscomp/CRL1.crlEgl

1 Answers

4
votes

The problem is that the certificates in the signer certificate chain present two URIs for CRL download, first a ldap URI and then a http URI, cf. @Egl's comment to your question, but that iText assumes it can simply take the first given URI and use System.Net.WebRequest to request its content.

Unfortunately WebRequest out-of-the-box only supports http:, https:, ftp:, and file: (cf. this msdn page). Thus, it fails iText's attempt to request the CRL via ldap with an Exception which iText doesn't catch.

You can make this work by

  • either registering a ldap WebRequest handler (cf. this msdn page).

    Caveat: While the msdn pages suggest that doing something like this is possible, I have not yet done it. Furthermore the OP could not easily follow this path. Thus, there might be restrictions beyond what the msdn documentation describes. Probably only web-ish protocols (ftp:, file:, http:, https:) can be used?

  • Or changing the iText verifiers (either actually changing iText classes or copying/deriving one's own CrlVerifier and LtvVerifier variants) to use the http-URI.

    This can either be implemented by catching such exceptions and then continuing with the next CRL request URI or (as done by the OP) by ignoring the ldap URI altogether (filtering it in CertificateUtil.GetCRLURL).