-1

In my application, I call the .NET method X509Certificate2.Verify on a client certificate and sometimes it returns True (valid) when it should not. This happens when I deliberately try to fool it.

I have code like this:

   ' Obtain a client cert in an application-dependent way.
   Dim clientCert As X509Certificate2 = GetX509Certificate2()
   ' Don't call .Verify on the entire certificate itself, as it will
   ' return False unless the client certificate itself is trusted 
   ' by the system, not just the issuer.  Instead, loop through the
   ' chain, considering the cert valid if any element of the chain
   ' is trusted.
   Dim chain As X509Chain = New X509Chain()
   chain.Build(clientCert)
   For Each element In chain.ChainElements
       ' ToDo: figure out why the Verify method apparently can be fooled 
       ' by a cert whose signer DN is the same as a trusted cert.
       isTrustedCertificate = element.Certificate.Verify() And
                              IsDateValid(element.Certificate)
       If isTrustedCertificate Then
           Exit For
       End If
   Next

This code considers a certificate valid if its issuer has the exact same distinguished name as a trusted certificate in the Window store. What's more, when I inspect the chain element corresponding to the bogus CA, it reports the thumbprint of the actual, valid CA rather than that of the bogus one. Weird.

Of course, it's easy to create your own bogus certificating authority certificate with an arbitrary DN. This means you can create your own client certificates and have them trusted by my application, as long as you know the DN of a trusted cert in my Windows store. Bad news.

I know that Windows has the ability to detect counterfeit certificates like this, because if I configure an IIS virtual directory to require client certificates and use a client cert issued by a bogus CA, IIS correctly returns an HTTP 403 error. However, within my application, the above .NET method is not as clever.

What am I doing wrong in my .NET code?

Thanks.


UPDATE:

It seems that my problem was two-fold:

  • clientCert.Verify() was returning False because the system was unable to find cert revocation information.
  • I failed to check the result of chain.Build(clientCert). It was returning True for the good cert, and False for the forged cert.

I don't know how to change the policy used by Verify on the entire cert, so I made these changes:

  • I set a non-default policy for chain.Build to use, ignoring problems when no revocation info was available.
  • I checked the result of chain.Build

Here's the final code:

Dim isTrustedCertificate As Boolean = False
Dim chain As X509Chain = New X509Chain()
' Set the chain policy to not complain if the revocation status is unknown.
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown _
     Or X509VerificationFlags.IgnoreEndRevocationUnknown
' The chain will fail to build if the issuer is untrusted.
' Checking the result of chain.Build seems to be important to detecting forged certs.
Dim bChainOK As Boolean = chain.Build(clientCert)
If bChainOK Then
    For Each element In chain.ChainElements
        isTrustedCertificate = element.Certificate.Verify() And
                                IsDateValid(element.Certificate)
        If isTrustedCertificate Then
            Exit For
        End If
    Next
End If
Mark R
  • 251
  • 3
  • 12

1 Answers1

0

According to the X509Certificate2.Verify documentation

This method builds a simple chain for the certificate and applies the base policy to that chain. If you need more information about a failure, validate the certificate directly using the X509Chain object.

So what are you doing wrong? You are building chain each time you call X509Certificate2.Verify(). Your bogus CA is never considered because Verify method builds its own chain using information from windows certificate store and verifies it using some default policy.

pepo
  • 8,644
  • 2
  • 27
  • 42