我的需求是更改AD的用户密码。因此,我按照 https://bl.ocks.org/magnetikonline/0ccdabfec58eb1929c997d22e7341e45 成功在 AD 域服务器上创建了 LDAP SSL 安全连接。
使用
ldp.exe
工具(在同一 AD 服务器上)我可以通过 SSL 连接。这意味着 AD 服务器上启用了 LDAPS。
现在我尝试使用位于客户端的库
Novell.Directory.Ldap
从 ASP.NET Core 应用程序连接它,使用以下代码:
public LdapConnection GetLDAPConnection(IOptions<ADConfiguration> _settings)
{
LdapConnection connection = new LdapConnection { SecureSocketLayer = true };
connection.Connect(_settings.Value.DomainIPAddress, _settings.Value.Port); //port is 636
connection.Bind(_settings.Value.AdminDn, _settings.Value.Password);
if (connection.Bound)
{
return connection;
}
return null;
}
Connect
方法抛出此错误:
System.Security.Authentication.AuthenticationException:“远程证书被提供的 RemoteCertificateValidationCallback 拒绝。”
客户端计算机是否也有 SSL 设置?或者我还缺少什么?请帮忙
我怀疑您的问题是使用域控制器的IP地址:
_settings.Value.DomainIPAddress
SSL/TLS 有两个目的:加密流量,以及验证服务器实际上是您想要与之通信的服务器。为了解决第二个目的,您用于连接的域名必须与证书中的域名匹配。在您的情况下,当它验证证书时,它会看到您连接到,比方说,
10.0.0.1
,但它从服务器获取的证书显示它是example.com
,并且验证失败,因为它不匹配。
您必须:
_settings.Value.DomainIPAddress
更改为证书中使用的域名。如果您没有该域名的 DNS 设置,您可以在 hosts
文件中添加一个条目。LdapConnection
忽略证书错误。数据仍将被加密,但不会验证证书(域不匹配、证书过期等)。不建议在生产应用程序中这样做,但这里有一个示例:https://stackoverflow.com/a/67818854/1202807下面的代码可以帮助我使用 LDAPS 连接到 AD
ldapConnection = new LdapConnection(new LdapDirectoryIdentifier("your.LDAPSserver.com", 636));
var networkCredential = new NetworkCredential("UsernameWithoutDomain", "yourPassword", "AD.yourDOMAIN.com");
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback);
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(networkCredential);
SearchRequest Srchrequest = new SearchRequest("CN=Users,DC=AD,DC=YOURCOMPANY,DC=COM", "[email protected]", System.DirectoryServices.Protocols.SearchScope.Subtree);
SearchResponse SrchResponse = (SearchResponse)ldapConnection.SendRequest(Srchrequest);
// 服务器回调
private static bool ServerCallback(LdapConnection connection, X509Certificate certificate)
{
return true;
}
令人惊讶的是,当我不使用 networkCredential 而仅使用 ldapConnection.Bind(); 时它也能工作; 似乎它在本地计算机上使用我的本地凭据作为默认凭据。
我写这个是为了在开发时可以轻松使用自签名证书。它支持纯文本、LDAPS 和 StartTLS。
using Novell.Directory.Ldap;
public class ActiveDirectory
{
private LdapConnection LdapConn = new();
public ActiveDirectory Connect(
string ldapServerAddr,
string ldapUser,
string ldapPass,
bool ldaps = false,
bool startTls = false,
bool trustAllCerts = false,
bool fallbackToInsecure = false
)
{
if (startTls && ldaps)
{
log.Fatal("Choose either StartTLS or LDAPS");
throw new Exception("Choose either StartTLS or LDAPS");
}
LdapConnectionOptions connOptions = new();
if (trustAllCerts)
{
log.Warn(
"Trusting all certificates. This is insecure and should not be used in production."
);
connOptions.ConfigureRemoteCertificateValidationCallback(
new System.Net.Security.RemoteCertificateValidationCallback(
(a, b, c, d) => true
)
);
}
int port = 389;
// Set the connection timeout to 5 seconds
// To-do: this does not seem to work. Investigate.
LdapConn.ConnectionTimeout = 5000;
bool performFallback = false;
/*
LDAPS (LDAP over SSL/TLS)
LDAPS secures the LDAP communication by running it over SSL/TLS.
It typically uses port 636 instead of the default LDAP port 389.
*/
if (ldaps)
{
log.Debug("Using LDAPS");
port = 636;
connOptions.UseSsl();
}
/*
LDAP with StartTLS
StartTLS is a mechanism to upgrade an existing, plaintext connection to
a secure TLS connection. It typically uses the default LDAP port 389
but negotiates TLS _after_ the initial connection.
*/
string connType = ldaps ? "LDAPS" : "LDAP";
log.Debug($"Connecting via {connType} to {ldapServerAddr}:{port}.");
if (startTls)
{
log.Debug("This connection will be upgraded to use secure transport with StartTLS");
}
// Connect to the LDAP server using either LDAP or LDAPS
LdapConn = new LdapConnection(connOptions);
try
{
// To-do Add multiple AD servers for failover. .Connect supports this natively.
LdapConn.Connect(ldapServerAddr, port);
log.Debug($"Connected to LDAP server {LdapConn.Host}:{LdapConn.Port}");
}
catch (System.Security.Authentication.AuthenticationException)
{
log.Error(
$"Certificate error connecting to LDAP server at {ldapServerAddr}:{port}"
);
if (fallbackToInsecure)
{
performFallback = true;
}
else
{
log.Warn("Unable to connect to LDAP by any configured method. Exiting.");
throw;
}
}
catch (Exception e)
{
log.Error(
$"Unable to connect to LDAP server at {ldapServerAddr}:{port}. Error: {e.Message}"
);
if (fallbackToInsecure)
{
performFallback = true;
}
else
{
log.Warn("Unable to connect to LDAP by any configured method. Exiting.");
throw;
}
}
if (startTls)
{
try
{
log.Debug("Enabling encryption with StartTLS");
LdapConn.StartTls();
log.Debug("StartTLS successful");
}
catch (LdapException e)
{
log.Error($"StartTLS failed. Error: {e.Message}");
if (fallbackToInsecure)
{
performFallback = true;
}
else
{
log.Error("StartTLS required. (Insecure fallback not enabled) Exiting.");
throw;
}
}
}
if (performFallback)
{
log.Warn("Falling back to insecure LDAP connection.");
try
{
Connect(
ldapServerAddr,
ldapUser,
ldapPass,
ldaps: false,
startTls: false,
fallbackToInsecure: false
);
return this;
}
catch (Exception e)
{
log.Error($"Fallback to insecure connection failed. Error: {e.Message}");
throw;
}
}
LdapConn.Bind(LdapConnection.LdapV3, ldapUser, ldapPass);
log.Debug($"Bind made to LDAP server {LdapConn.Host}:{LdapConn.Port} as {ldapUser}");
return this;
}
}