admin管理员组

文章数量:1405510

Summary of this question: does someone know how to use a client certificate to authenticate over LDAP to Active Directory without using a password from a dotnet application? Or at least can someone shed some light on how this is supposed to work?

What we are trying to accomplish:

  1. From a dotnet application using LdapConnection create a LDAP connection to Active Directory outside of my current domain
  2. Using only a username and a certificate for authentication
  3. Verifying if the user is in the correct group We are using System.DirectoryServices.Protocols 6.0.2 on 8.0 on Windows 11

The documentation seems to contradict each other, because this document () seems to suggest a bind on an client certificate authenticated TLS connection is not allowed, in contrast the RFC seems to suggest its mandatory to send a bind: .

We hit a bug in the library initially which was picked up, but I'm not sure if it was the only one (see ).

Our attempts: Connecting with TLS on port 636 results in Bind failing on "Authentication method not supported".

string ldapPath = "dc";
int LDAPPort = 636;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
LdapSessionOptions options = ldapConnection.SessionOptions;
options.SecureSocketLayer = true;
options.ProtocolVersion = 3;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.Bind();
// Perform further actions after this

Connecting without TLS on port 389 and starting TLS afterwards results in Bind failing on "Authentication method not supported".

string ldapPath = "dc";
int LDAPPort = 389;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.SessionOptions.StartTransportLayerSecurity(null);
ldapConnection.Bind();
// Perform further actions after this

Connecting with TLS on port 636, skipping the bind and turning autobind off results in search failing with "In order to perform this operation a successful bind must be completed on the connection".

string ldapPath = "dc";
int LDAPPort = 636;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.AutoBind = false;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.SendRequest(...)

Summary of this question: does someone know how to use a client certificate to authenticate over LDAP to Active Directory without using a password from a dotnet application? Or at least can someone shed some light on how this is supposed to work?

What we are trying to accomplish:

  1. From a dotnet application using LdapConnection create a LDAP connection to Active Directory outside of my current domain
  2. Using only a username and a certificate for authentication
  3. Verifying if the user is in the correct group We are using System.DirectoryServices.Protocols 6.0.2 on 8.0 on Windows 11

The documentation seems to contradict each other, because this document (https://learn.microsoft/en-us/openspecs/windows_protocols/ms-adts/8e73932f-70cf-46d6-88b1-8d9f86235e81) seems to suggest a bind on an client certificate authenticated TLS connection is not allowed, in contrast the RFC seems to suggest its mandatory to send a bind: https://datatracker.ietf./doc/html/rfc2830#ref-AuthMeth.

We hit a bug in the library initially which was picked up, but I'm not sure if it was the only one (see https://github/dotnet/runtime/issues/113154#issuecomment-2705206987).

Our attempts: Connecting with TLS on port 636 results in Bind failing on "Authentication method not supported".

string ldapPath = "dc";
int LDAPPort = 636;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
LdapSessionOptions options = ldapConnection.SessionOptions;
options.SecureSocketLayer = true;
options.ProtocolVersion = 3;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.Bind();
// Perform further actions after this

Connecting without TLS on port 389 and starting TLS afterwards results in Bind failing on "Authentication method not supported".

string ldapPath = "dc";
int LDAPPort = 389;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.SessionOptions.StartTransportLayerSecurity(null);
ldapConnection.Bind();
// Perform further actions after this

Connecting with TLS on port 636, skipping the bind and turning autobind off results in search failing with "In order to perform this operation a successful bind must be completed on the connection".

string ldapPath = "dc";
int LDAPPort = 636;
var username = "user";
using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
ldapConnection.AuthType = AuthType.External;
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.AutoBind = false;
ldapConnection.ClientCertificates.Add(cert);
ldapConnection.SendRequest(...)
Share Improve this question asked Mar 7 at 15:00 David van LuijkDavid van Luijk 11 bronze badge 2
  • That sounds like one of those odd AD-specific behaviors, a bit like its handling of GSSAPI sign/seal when used over TLS. Note though that both pages talk about a SASL bind using the 'EXTERNAL' SASL mechanism – not about a credential-less 'Simple' bind like you're doing. (Although on that note, I wonder why you don't want to use Kerberos, i.e. SASL 'GSSAPI' or 'GSS-SPNEGO' bind?) – grawity_u1686 Commented Mar 8 at 14:18
  • @grawity_u1686 We tried to put the AuthType to Kerberos/Negotiate but we couldn't get that to work. We are not bound to the AuthType External, we thought this was the appropriate value given the documentation. Maybe it's due to the fact where are trying to connect from outside the domain? If this is "Simple" bind, can we get SASL bind to work using LdapConnection? – David van Luijk Commented Mar 10 at 15:45
Add a comment  | 

1 Answer 1

Reset to default 0

For anyone stumbling across this same issue: the option "ReferralChasing" needed to be set to "None" after which the beneath code worked.

using System.DirectoryServices.Protocols;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace AuthTest;

public class SystemLdap
{
    public static void Run(X509Certificate2 smartCardCert)
    {
        Console.WriteLine($"Running SystemLdap...{smartCardCert.Subject}");
        string ldapPath = "dc";
        int LDAPPort = 636;

        using LdapConnection ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(ldapPath, LDAPPort));
        ldapConnection.AuthType = AuthType.External;
        ldapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
        ldapConnection.SessionOptions.SecureSocketLayer = true;
        ldapConnection.SessionOptions.ProtocolVersion = 3;
        ldapConnection.ClientCertificates.Add(smartCardCert);

        try
        {
            var username = "user";

            // Perform a search
            var searchRequest = new SearchRequest(
                $"DC=dc,DC=nl",
                $"(&(objectClass=user)(|(cn={username})(sAMAccountName={username})))",
                SearchScope.Subtree
            );

            var response = (SearchResponse)ldapConnection.SendRequest(searchRequest);

            foreach (SearchResultEntry entry in response.Entries)
            {
                Console.WriteLine($"User found: {entry.DistinguishedName}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"LDAP Authentication failed: {ex}");
        }
    }
}

本文标签: netDotNet LdapConnection with authentication method external and client certificateStack Overflow