Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net program does not get validated server certificate

Tags:

c#

.net

ssl

ldap

I have a rather simple LDAP client that works ok when connecting to the 389 port (LDAP) but fails with a "LDAP server unavailable" when I try to connect to the 636 port (LDAPS).

namespace MyNS
{
  class ProgramLdap
  {
    private static LdapConnection CreateConnection(String baseDn, string usuario, string password)
    {
      LdapConnection ldapConnection = new LdapConnection(
        new LdapDirectoryIdentifier("myserver.example", 636, true, false));
      ldapConnection.SessionOptions.SecureSocketLayer = true;
      ldapConnection.SessionOptions.ProtocolVersion = 3;
      // [CODE MODIFICATION HERE]
      ldapConnection.Credential = new NetworkCredential(usuario, password);
      ldapConnection.AuthType = AuthType.Basic;
      ldapConnection.Timeout = new TimeSpan(1, 0, 0);
      return ldapConnection;
    }

    static void Main(string[] args)
    {

      LdapConnection ldapConnection = CreateConnection("", "myLdapUser", "noneOfYourBusiness");
      SearchRequest searchRequest = new SearchRequest(
        "ou=usuarios,dc=Dexter,dc=local",
         String.Format("(&(objectclass=person)(cn={0}))", user),
         SearchScope.Subtree,
         new string[0]);
       SearchResponse searchResponse =
          (SearchResponse) ldapConnection.SendRequest(searchRequest);
      System.Diagnostics.Debug.WriteLine("Resultados " + searchResponse.Entries.Count);
    }
  }
}

If I add the following at [CODE MODIFICATION HERE] to accept all server certificates, it works:

ldapConnection.SessionOptions.VerifyServerCertificate =
   new VerifyServerCertificateCallback((conn, certificate) => true);

The certificate is signed by a self-signed CA, I have added the CA public certificate to the Local Computer list of "Trusted Root Certification Authorities"1.

If I check the server certificate with openSSL using that CA's certificate, it validates it. Also, I have tried LdapAdmin and when the CA is in that list, no warning is shown when connecting to the LDAP server.

If I use the VerifyServerCertificateCallback to print the contents of the certificate:

ldapConnection.SessionOptions.VerifyServerCertificate =
  new VerifyServerCertificateCallback(
    (conn, certificate) => {
       X509Certificate2 certificate2 = new X509Certificate2(certificate);
       bool verify = certificate2.Verify();
       System.Diagnostics.Debug.WriteLine(
         String.Format(
           "certificate2.Verify {0}; Name {1}; NameOID {2}; FriendlyName {3}; Thumbprint {4}; Algorithm FriendlyName {5}",
           verify,
           certificate2.SubjectName.Name,
           certificate2.SubjectName.Oid,
           certificate2.FriendlyName,
           certificate2.Thumbprint,
           certificate2.SignatureAlgorithm.FriendlyName));

           foreach (X509Extension extension in certificate2.Extensions) 
           {
             System.Diagnostics.Debug.WriteLine(extension.ToString() + " " + extension.Oid.FriendlyName + " " + Encoding.UTF8.GetString(extension.RawData));
            }
            return verify;
         });

it shows me the thumbprint of the server certificate but yet verify fails.

What can I be? It seems that I am missing something very basic, but I cannot understand what.


UPDATE:

I checked @FrankNielsen's suggestion and I added this code in the VerifyServerCertificateCallback:

(conn, certificate) => {
  X509Chain ch = new X509Chain();
  ch.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
  ch.Build(new X509Certificate2(certificate));
  System.Diagnostics.Debug.WriteLine("Chain Information");
  System.Diagnostics.Debug.WriteLine(String.Format("Chain revocation flag: {0}", ch.ChainPolicy.RevocationFlag));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain revocation mode: {0}", ch.ChainPolicy.RevocationMode));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain verification flag: {0}", ch.ChainPolicy.VerificationFlags));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain verification time: {0}", ch.ChainPolicy.VerificationTime));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain status length: {0}", ch.ChainStatus.Length));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain application policy count: {0}", ch.ChainPolicy.ApplicationPolicy.Count));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain certificate policy count: {0} {1}", ch.ChainPolicy.CertificatePolicy.Count, Environment.NewLine));

  System.Diagnostics.Debug.WriteLine("Chain Element Information");
  System.Diagnostics.Debug.WriteLine(String.Format("Number of chain elements: {0}", ch.ChainElements.Count));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain elements synchronized? {0} {1}", ch.ChainElements.IsSynchronized, Environment.NewLine));
  foreach (X509ChainElement element in ch.ChainElements)
  {
    System.Diagnostics.Debug.WriteLine(String.Format("Element issuer name: {0}", element.Certificate.Issuer));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate valid from: {0}", element.Certificate.NotBefore));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate valid until: {0}", element.Certificate.NotAfter));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate is valid: {0}", element.Certificate.Verify()));
    System.Diagnostics.Debug.WriteLine(String.Format("Element error status length: {0}", element.ChainElementStatus.Length));
    System.Diagnostics.Debug.WriteLine(String.Format("Element information: {0}", element.Information));
    System.Diagnostics.Debug.WriteLine(String.Format("Thumbprint: {0}", element.Certificate.Thumbprint));
    System.Diagnostics.Debug.WriteLine(String.Format("Number of element extensions: {0}{1}", element.Certificate.Extensions.Count, Environment.NewLine));
    if (ch.ChainStatus.Length > 1)
    {
      for (int index = 0; index < element.ChainElementStatus.Length; index++)
      {
         System.Diagnostics.Debug.WriteLine(element.ChainElementStatus[index].Status);
        System.Diagnostics.Debug.WriteLine(element.ChainElementStatus[index].StatusInformation);
       }
     }
   }
   return true;
 });

And it returns:

Chain Information
Chain revocation flag: ExcludeRoot Chain revocation mode: NoCheck
Chain verification flag: NoFlag
Chain verification time: 07/10/2019 15:53:00
Chain status length: 0
Chain application policy count: 0
Chain certificate policy count: 0

Chain Element Information
Number of chain elements: 2
Chain elements synchronized? False

Element issuer name: CN=dexter-SCPDPRDEXTER01V-CA, DC=dexter, DC=local
Element certificate valid from: 02/09/2019 12:24:22
Element certificate valid until: 01/09/2020 12:24:22
Element certificate is valid: False
Element error status length: 0
Element information:
Thumbprint: 63DCF4EFE0C96EF021BCC9CE662E2627A3CDF399
Number of element extensions: 9

Element issuer name: CN=dexter-SCPDPRDEXTER01V-CA, DC=dexter, DC=local
Element certificate valid from: 11/06/2019 7:39:01
Element certificate valid until: 11/06/2069 7:49:01
Element certificate is valid: True
Element error status length: 0 Element information:
Thumbprint: 7BD9C718E336A50FA006CAEF539895C7E3EA5DA0
Number of element extensions: 4

The certificates match what would be expected (the CA is retrieved), the CA return true to Verify() but the server certificate returns false to Verify().


1And for good measure, I also did try adding it to "Intermediate Certification Authorities" to no avail.

like image 462
SJuan76 Avatar asked Sep 30 '19 09:09

SJuan76


1 Answers

Finally the way to properly debug it was inspecting the elements of ChainStatus, as stated in X509Certificate2.Verify() returns false always

That way I did found that my program could not connect to the Certificate Revocation List URL.

The solution (other than opening access to the URL) is to validate the certificate in the callback using the X509Chain class and setting it not to check CRLs:

ldapConnection.SessionOptions.VerifyServerCertificate =
    new VerifyServerCertificateCallback(
        (conn, certificate) =>
            {
                X509Chain x509Chain = new X509Chain();
                x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                X509Certificate2 cert2 = new X509Certificate2(certificate);
                bool buildResult = x509Chain.Build(cert2);
                if (!buildResult)
                {
                    foreach (X509ChainStatus chainStatus in x509Chain.ChainStatus)
                    {
                        System.Diagnostics.Debug.WriteLine(
                            String.Format(
                                "Chain Status {0} : {1}", chainStatus.Status, chainStatus.StatusInformation));
                    }
                }
                return buildResult;
            });
like image 80
SJuan76 Avatar answered Oct 28 '22 22:10

SJuan76