2012-06-01 139 views
14

我试图使用.NET 3.5 System.DirectoryServices.AccountManagement命名空间通过SSL加密的LDAP连接对我们的Active Directory LDAP服务器验证用户凭据。这里的示例代码:如何通过LDAP + SSL验证Active Directory信用?

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:389", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate)) 
{ 
    return pc.ValidateCredentials(_username, _password); 
} 

此代码通过不安全的LDAP(端口389)的罚款,但我宁愿不以明文形式传输用户名/密码组合。但是,当我更改为LDAP + SSL(端口636),我得到以下异常:

System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests. 
    at System.DirectoryServices.Protocols.ErrorChecking.CheckAndSetLdapError(Int32 error) 
    at System.DirectoryServices.Protocols.LdapSessionOptions.FastConcurrentBind() 
    at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions) 
    at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password) 
    at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password) 
    at (my code) 

端口636适用于其他活动,如查找非密码信息为LDAP/AD项...

UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, _username) 

...所以我知道这不是我的LDAP服务器的SSL设置,因为它可以通过SSL进行其他查找。

是否有人通过SSL调用ValidateCredentials(...)调用?你能解释一下吗?或者有另一种/更好的方法来安全验证AD/LDAP凭证?

+0

以下是有关通过SSL对LDAP进行故障诊断的MSDN文章:http://support.microsoft.com/kb/938703 – CAbbott

+0

感谢您的链接。但是,我可以通过LDAPS(端口636)进行通信,这对我已经执行的所有其他LDAP查询都很好。这似乎是有关'ValidateCredentials()'的一些不寻常的事情。不过,我会更详细地阅读文章。 –

+0

除非LDAP客户端提供密码质量和历史检查,否则密码应以明文形式传输 - 不能散列 - 通过与支持密码质量检查和密码历史记录实施的服务器的安全连接进行传输,否则,服务器将无法执行质量和历史。 –

回答

14

感谢同事,我能够使用System.DirectoryServices.Protocols名称空间验证凭据。下面的代码:

// See http://support.microsoft.com/kb/218185 for full list of LDAP error codes 
const int ldapErrorInvalidCredentials = 0x31; 

const string server = "sd.example.com:636"; 
const string domain = "sd.example.com"; 

try 
{ 
    using (var ldapConnection = new LdapConnection(server)) 
    { 
     var networkCredential = new NetworkCredential(_username, _password, domain); 
     ldapConnection.SessionOptions.SecureSocketLayer = true; 
     ldapConnection.AuthType = AuthType.Negotiate; 
     ldapConnection.Bind(networkCredential); 
    } 

    // If the bind succeeds, the credentials are valid 
    return true; 
} 
catch (LdapException ldapException) 
{ 
    // Invalid credentials throw an exception with a specific error code 
    if (ldapException.ErrorCode.Equals(ldapErrorInvalidCredentials)) 
    { 
     return false; 
    } 

    throw; 
} 

我不激动与使用try/catch块来控制的决策逻辑,但它的作品。 :/

1

也许这是另一种方式。验证凭证中没有任何异常。 ContextOptions必须正确设置。

默认值:

ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing

添加SSL:

ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing | ContextOptions.SecureSocketLayer

ContextOptions.Negotiate或ContextOptions.SimpleBind是必需的。或者无论您的服务器需要执行身份验证。 ContextOptions仅支持OR比特。

您可以尝试直接在ValidateCredentials方法中以这种方式设置ContextOptions。

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate | ContextOptions.SecureSocketLayer)) 
{ 
    return pc.ValidateCredentials(_username, _password); 
} 

或者

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate)) 
{ 
    return pc.ValidateCredentials(_username, _password, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer); 
} 
+0

我看到你说“或者你的服务器需要执行认证。”我如何知道服务器需要进行身份验证?我正在使用以前设置了一些选项的服务器,但我不知道他们现在是什么。我可以检查他们吗? –

0

对我来说,ValidateCredentials方法工作得很好。我发现,问题出在托管AD的服务器上(我正在使用AD LDS)。您需要将服务器证书与AD实例相关联。因此,如果您的实例被称为“MyAD”​​(或ActiveDirectoryWebService),则需要打开MMC,并单击“证书”模块,选择“服务帐户”,然后从列表中选择“MyAD”​​。从那里您可以将SSL证书添加到“MyAD”​​个人商店中。这终于使SSL处理陷入困境。

我怀疑,从我知道LdapConnection方法和你省略了回调函数,你是不是验证服务器证书的事实。这是一个杂乱的工作,并且ValidateCredentials是免费的。可能不是什么大不了的事情,但是一个安全漏洞无处不在。

相关问题