2
votes

I'm trying to verify digital signatures in c# using iTextSharp.

I've followed the example in the iText web (http://gitlab.itextsupport.com/itextsharp/tutorial/blob/master/signatures/chapter5/C5_03_CertificateValidation/C5_03_CertificateValidation.cs) but the results aren't what I expected. Specifically, when trying to verify a signature through OCSP or CRL, the result usually is that the signature couldn't be verified. I think that shouldn't happen, since Adobe Reader verificates the signatures ok.

The pdf I'm using to test the verification can be found in this link: https://blogs.adobe.com/security/SampleSignedPDFDocument.pdf

This is the code I'm using (short version of the example from the link above):

static void Main(String[] args)
{
    LoggerFactory.GetInstance().SetLogger(new SysoLogger());
    C5_03_CertificateValidation app = new C5_03_CertificateValidation();
    app.VerifySignatures(EXAMPLE); //Pdf file I'm using to test the verification
}

public void VerifySignatures(String path)
{
    Console.WriteLine(path);
    PdfReader reader = new PdfReader(path);
    AcroFields fields = reader.AcroFields;
    List<String> names = fields.GetSignatureNames();
    foreach (string name in names)
    {
        Console.WriteLine("===== " + name + " =====");
        VerifySignature(fields, name);
    }
    Console.WriteLine();
}

public PdfPKCS7 VerifySignature(AcroFields fields, String name)
{
    PdfPKCS7 pkcs7 = fields.VerifySignature(name);
    X509Certificate[] certs = pkcs7.SignCertificateChain;
    DateTime cal = pkcs7.SignDate;

    X509Certificate signCert = certs[0];
    X509Certificate issuerCert = (certs.Length > 1 ? certs[1] : null);
    Console.WriteLine("=== Checking validity of the document at the time of signing ===");
    CheckRevocation(pkcs7, signCert, issuerCert, cal);
    Console.WriteLine("=== Checking validity of the document today ===");
    CheckRevocation(pkcs7, signCert, issuerCert, DateTime.Now);
    return pkcs7;
}

public static void CheckRevocation(PdfPKCS7 pkcs7, X509Certificate signCert, X509Certificate issuerCert, DateTime date)
{
    List<BasicOcspResp> ocsps = new List<BasicOcspResp>();
    if (pkcs7.Ocsp != null)
        ocsps.Add(pkcs7.Ocsp);
    OcspVerifier ocspVerifier = new OcspVerifier(null, ocsps);
    List<VerificationOK> verification =
        ocspVerifier.Verify(signCert, issuerCert, date);
    if (verification.Count == 0)
    {
        List<X509Crl> crls = new List<X509Crl>();
        if (pkcs7.CRLs != null)
            foreach (X509Crl crl in pkcs7.CRLs)
                crls.Add(crl);
        CrlVerifier crlVerifier = new CrlVerifier(null, crls);
        verification.AddRange(crlVerifier.Verify(signCert, issuerCert, date));
    }
    if (verification.Count == 0)
        Console.WriteLine("The signing certificate couldn't be verified with the example");
    else
        foreach (VerificationOK v in verification)
            Console.WriteLine(v);


    //Code not in the example, added by me
    //This way, I can find out if the certificate is revoked or not (through CRL). Not sure if it's the right way though
    if (verification.Count == 0 && pkcs7.CRLs != null && pkcs7.CRLs.Count != 0)
    {
        bool revoked = false;
        foreach (X509Crl crl in pkcs7.CRLs)
        {
            revoked = crl.IsRevoked(pkcs7.SigningCertificate);
            if (revoked)
                break;
        }

        Console.WriteLine("Is certificate revoked?: " + revoked.ToString());
    }
}

And this is the output I get:

===== Signature2 =====

=== Checking validity of the document at the time of signing ===
i.t.p.s.OcspClientBouncyCastle INFO  Getting OCSP from http://adobe-ocsp.geotrust.com/responder
iTextSharp.text.pdf.security.OcspClientBouncyCastle ERROR Error en el servidor remoto: (502) Puerta de enlace no válida.
i.t.p.s.OcspVerifier INFO  Valid OCSPs found: 0
i.t.p.s.CrlVerifier INFO  Getting CRL from http://crl.geotrust.com/crls/adobeca1.crl
i.t.p.s.CrlVerifier INFO  Valid CRLs found: 0
The signing certificate couldnt be verified with the example
Is certificate revoked?: False

=== Checking validity of the document today ===
i.t.p.s.OcspClientBouncyCastle INFO  Getting OCSP from http://adobe-ocsp.geotrust.com/responder
iTextSharp.text.pdf.security.OcspClientBouncyCastle ERROR Error en el servidor remoto: (502) Puerta de enlace no válida.
i.t.p.s.OcspVerifier INFO  Valid OCSPs found: 0
i.t.p.s.CrlVerifier INFO  Getting CRL from http://crl.geotrust.com/crls/adobeca1.crl
i.t.p.s.CrlVerifier INFO  Valid CRLs found: 0
The signing certificate couldnt be verified with the example
Is certificate revoked?: False

I don't get why the signature couldn't be verified, since Adobe can do it. Any ideas are appreciated.

1
If you look closely at the outputs of Adobe Reader, you'll see "The selected certificate is considered valid because it does not appear in the Certificate Revocation List (CRL) that is embedded in the signature." Your code tries to retrieve OCSP responses or CRLs online.mkl
@mkl I checked and you're right. But the signature also has and OCSP url and a CRL url (I understand that reading the output), I don't understand why the signature couldn't be verified using those. Could it be that the urls aren't valid? Also, I don't get why the crlVerifier didn't give an answer but the crl.IsRevoked did. (btw, thanks for quick answer)joaquincasal
"But the signature also has and OCSP url and a CRL url (I understand that reading the output), I don't understand why the signature couldn't be verified using those." - A The OCSP service URL does not exist anymore; the service seems to have been discontinued. That's why one should add validation related information to the PDF itself. B The CRL you currently retrieve from that URL is valid from 2015-01-22. The signer certificate is marked valid not after 2009-09-04. thus, no usable information about the certificate can be expected from that CRL. ...mkl
The CRL embedded into the signature is valid from 2009-07-16 which is the date of the signature creation. That CRL, therefore, does contain usable information about the certificate.mkl
Hhmmm, I just realized that (even though it is mostly true) the above said does not explain your test results. I'll write up something more detailed later.mkl

1 Answers

1
votes

There are two aspects to this question:

  • Why does iTextSharp appear not to be able to verify the given example signature provided by Adobe?
  • At which date, using which CRL, should revocation checks be made?

Verifying the given signature using iTextSharp

Using the sample code from C5_03_CertificateValidation.cs the OP couldn't verify that the certificates in question, in particular the signer certificate, was not revoked, neither "at the time of signing" nor "today". I, on the other hand, immediately could verify that "at the time of signing".

The major difference between the OP's tests and my tests were that the former ones occurred using the OP's time zone UTC-3 while mine were using UTC+2.

Thus, I ran the code using different system time zones, and indeed: The verification only succeeded in time zones UTC-01 and up, i.e. UTC-01, UTC, UTC+01, ...

The time returned by DateTime cal = pkcs7.SignDate in the tests turned out to be given using the current local time zone.

Apparently, therefore, the CRL validation code does not use times according to the local time zone but instead in some fixed time zone, presumably in UTC itself.

Thus, one can make the example code work universally by

changing

crlVerifier.Verify(signCert, issuerCert, date)

to

crlVerifier.Verify(signCert, issuerCert, date.ToUniversalTime())

as the OP could confirm after tests.

Choosing the right date and CRL for revocation checks

The OP mentions that he would prefer to execute revocation checks using current time and current versions of the CRL of the PKI in question.

While this approach appears to make use of the most up-to-date information available, its usefulness is limited:

  • If the certificate under scrutiny is beyond its original validity period now (i.e. its valid-not-after date is in the past) but was not at the time of signing, information about its possible revocation at the time of signing may not be on the CRL anymore. Indeed, according to RFC 5280

    An entry MUST NOT be removed from the CRL until it appears on one regularly scheduled CRL issued beyond the revoked certificate's validity period.

    By implication, therefore, it may be removed from the CRL in any version issued after the validity period.

    Thus, it makes no sense to use a CRL newer than the end of the validity period of the certificate.

  • Even if the certificate under scrutiny is not yet beyond its original validity period, the PKI provider might have gone out of business. In that case all access points for revocation information now might only serve a final version of the CRL created when the PKI was still active or none at all.

    In that case one can use that final CRL or the newest CRL one still has available (e.g. in some CRL cache) as long as the CRL is recent enough.

Thus, validation policies often allow and even require the use of an older CRL as long as it is recent enough, at least the CRL's nextUpdate value must be after the signing time, and at the same time old enough, at least the CRL's thisUpdate value must be before the end of the validity period of the certificate.

One item to consider, though, is what one considers to be the signing time to compare with when checking whether an older CRL can be used.

The pkcs7.SignDate used in the iText example code above may merely be the content of a field in the PDF or the CMS container which might have been faked: Someone might have gotten hold of the private key; after the associated certificate has been revoked, that person might still misuse the key setting the signing time information to a date before revocation.

Thus, you might determine the signing date differently. E.g.

  • if the signature container contains a signature time stamp or the PDF document contains a later document time stamp, and if that time stamp can be trusted, the time of that time stamp can be used;

  • if the signed document has been locally stored for some time (e.g. in some archiving system) and the time of storage is known and can be trusted, that time can be used;

  • ...