5

我有一个应用程序使用典型的 System.DirectoryServices.DirectoryEntry 代码从目录服务复制数据。我现在需要使用带有自签名证书的 SSL 从 Novell eDirectory 进行复制。我怀疑现有代码可以使用可以验证的有效证书,或者如果将自签名证书添加到本地计算机密钥库中。然而,为了确保它与自签名证书一起工作,我能找到的唯一解决方案是使用 System.DirectoryServices.Protocols 命名空间和 LdapConnection 类,从而我可以连接一个 VerifyServerCertificate 回调。我找不到任何方法将相同的概念应用于 DirectoryEntry 实例,或者与 LdapConnection 实例连接并以某种方式将其“转换”为 DirectoryEntry 实例。也许它不是 不可能,我只是想确认一下。欢迎任何其他想法。

我发现的唯一相关链接是:http: //www.codeproject.com/Articles/19097/eDirectory-Authentication-using-LdapConnection-and

4

3 回答 3

10

This is a phenomenal question.

I've been battling this same issue for a few days now, and I've finally got some definitive proof on why the DirectoryEntry object will not work in this scenario.

This particular Ldap server (running on LDAPS 636) also issues it's own self signed certificate. Using LdapConnection (and monitoring the traffic via Wireshark), I noticed a handshake taking place that does not occur when using DirectoryEntry :

enter image description here

The first sequence is the from the secured ldap server, the second sequence is from my machine. The code that prompts the second sequence is :

ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };

There are others way to "fake out" the callback, but this what I've been using.

Unfortunately, DirectoryEntry does not have an option or method to verify a self signed cert, thus the acceptance of the certificate never happens (second sequence), and the connection fails to initialize.

The only feasible way to accomplish this is by using LdapConnection, in conjunction with a SearchRequest and SearchResponse. This is what I've got so far :

LdapConnection ldapConnection = new LdapConnection("xxx.xxx.xxx:636");

var networkCredential = new NetworkCredential("Hey", "There", "Guy");
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(networkCredential);

SearchRequest request = new SearchRequest("DC=xxx,DC=xxx,DC=xxx", "(sAMAccountName=3074861)", SearchScope.Subtree);
SearchResponse response = (SearchResponse)ldapConnection.SendRequest(request);

if(response.Entries.Count == 1)
{SearchResultEntry entry = response.Entries[0];
 string DN = entry.DistinguishedName;}

From there you can gather AD Properties from the SearchResponse, and process accordingly. This is a total bummer though, because the SearchRequest seems to be much slower then using the DirectoryEntry.

Hope this helps!

于 2012-10-25T16:47:07.753 回答
3

我保证,这将是我关于这个特定问题的最后一篇文章。:)

经过一周的研究和开发,我有一个很好的解决方案,到目前为止它对我来说非常有效。

该方法与我的第一个答案有些不同,但总的来说,它是相同的概念;使用 LdapConnection 强制验证证书。

//I set my Domain, Filter, and Root-AutoDiscovery variables from the config file
string Domain = config.LdapAuth.LdapDomain;
string Filter = config.LdapAuth.LdapFilter;
bool AutoRootDiscovery = Convert.ToBoolean(config.LdapAuth.LdapAutoRootDiscovery);

//I start off by defining a string array for the attributes I want 
//to retrieve for the user, this is also defined in a config file.
string[] AttributeList = config.LdapAuth.LdapPropertyList.Split('|');

//Delcare your Network Credential with Username, Password, and the Domain
var credentials = new NetworkCredential(Username, Password, Domain);

//Here I create my directory identifier and connection, since I'm working 
//with a host address, I set the 3rd parameter (IsFQDNS) to false
var ldapidentifier = new LdapDirectoryIdentifier(ServerName, Port, false, false);
var ldapconn = new LdapConnection(ldapidentifier, credentials);

//This is still very important if the server has a self signed cert, a certificate 
//that has an invalid cert path, or hasn't been issued by a root certificate authority. 
ldapconn.SessionOptions.VerifyServerCertificate += delegate { return true; };

//I use a boolean to toggle weather or not I want to automatically find and query the absolute root. 
//If not, I'll just use the Domain value we already have from the config.
if (AutoRootDiscovery)
{
    var getRootRequest = new SearchRequest(string.Empty, "objectClass=*", SearchScope.Base, "rootDomainNamingContext");
    var rootResponse = (SearchResponse)ldapconn.SendRequest(getRootRequest);
    Domain = rootResponse.Entries[0].Attributes["rootDomainNamingContext"][0].ToString();
}

//This is the filter I've been using : (&(objectCategory=person)(objectClass=user)(&(sAMAccountName={{UserName}})))
string ldapFilter = Filter.Replace("{{UserName}}", UserName);

//Now we can start building our search request
var getUserRequest = new SearchRequest(Domain, ldapFilter, SearchScope.Subtree, AttributeList);

//I only want one entry, so I set the size limit to one
getUserRequest.SizeLimit = 1;

//This is absolutely crucial in getting the request speed we need (milliseconds), as
//setting the DomainScope will suppress any refferal creation from happening during the search
SearchOptionsControl SearchControl = new SearchOptionsControl(SearchOption.DomainScope);
getUserRequest.Controls.Add(SearchControl);

//This happens incredibly fast, even with massive Active Directory structures
var userResponse = (SearchResponse)ldapconn.SendRequest(getUserRequest);

//Now, I have an object that operates very similarly to DirectoryEntry, mission accomplished  
SearchResultEntry ResultEntry = userResponse.Entries[0];

我在这里要注意的另一件事是 SearchResultEntry 将返回用户“属性”而不是“属性”。

属性以字节数组的形式返回,因此您必须对它们进行编码才能获得字符串表示形式。值得庆幸的是,System.Text.Encoding 包含一个本机 ASCIIEncoding 类,可以很容易地处理这个问题。

string PropValue = ASCIIEncoding.ASCII.GetString(PropertyValueByteArray);

就是这样!很高兴终于解决了这个问题。

干杯!

于 2012-11-09T16:58:03.090 回答
0

我使用下面的代码使用 DirectoryEntry 与 ldaps 连接。

我在我的场景中理解的是,当在服务器路径中指定ldaps或身份验证类型被称为“AuthenticationTypes.SecureSocketsLayer”时,directoryEntry 不起作用,但如果在服务器名称末尾只提到 ldaps 端口,它就可以工作。在查看了wireshark日志后,我可以看到上面提到的握手发生了。

握手: 在此处输入图像描述

代码:

public static SearchResultCollection GetADUsers()
    {
        try
        {
            List<Users> lstADUsers = new List<Users>();
            DirectoryEntry searchRoot = new DirectoryEntry("LDAP://adserver.local:636", "username", "password");
            DirectorySearcher search = new DirectorySearcher(searchRoot);
            search.PropertiesToLoad.Add("samaccountname");
            SearchResult result;
            SearchResultCollection resultCol = search.FindAll();
            Console.WriteLine("Record count " + resultCol.Count);
            return resultCol;
        }
        catch (Exception ex)
        {
            Console.WriteLine("exception" + ex.Message);
            return null;
        }
    }
于 2015-11-03T11:36:31.937 回答