【问题标题】:How to get a DirectoryEntry from LDAP over SSL?如何通过 SSL 从 LDAP 获取 DirectoryEntry?
【发布时间】:2015-02-19 14:11:39
【问题描述】:

我正在尝试从 LDAP 获取根 DirectoryEntry,以便显示它的漂亮图形树视图。

在正常连接下一切正常,但我无法使用 SSL。

var root = this.checkBoxSSL.Checked
                    ? new DirectoryEntry("LDAP://" + this.textBoxServer.Text,
                        this.textBoxUsername.Text,
                        this.textBoxPassword.Text,
                        AuthenticationTypes.SecureSocketsLayer)
                    : new DirectoryEntry("LDAP://" + this.textBoxServer.Text,
                        this.textBoxUsername.Text,
                        this.textBoxPassword.Text);
var dn = root.Properties["distinguishedName"].Value;

等等……

但我收到“服务器无法运行”异常。这一切似乎都归结为绑定过程。根据互联网研究,这可能是证书和/或身份验证方法(NTLM 等)的问题。

那么如何通过 SSL 获得有效的 DirectoryEntry?

我愿意接受替代解决方案,只要我可以检索我需要的节点的所有 LDAP 属性。 (根、DC、OU、CN、组和用户)

编辑: 看来问题归结为 SSL 证书。我们只有一个自签名证书 atm。这似乎被.NET默认拒绝了。稍后我们将使用正确签名的证书尝试此操作,但我可能也需要能够处理自签名证书。

这是我对证书的了解有限的地方。我目前正在探索一种不同的代码解决方案,因为它似乎是唯一允许我影响整个证书处理的解决方案:

System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new System.Security.Cryptography.X509Certificates.X509Certificate2();
cert.Import("..\\..\\test certificate.cer");

LdapConnection con = new LdapConnection("ip:636");
con.Credential = new NetworkCredential("un", "pw");
con.AuthType = AuthType.Ntlm;
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((ldapcon, cer) => {
    var cer2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(cer);

    StringBuilder strb = new StringBuilder();

    strb.AppendFormat("{0} {1} matches: {2}\n", "Subject", cert.Subject, cert.Subject.Equals(cer2.Subject));
    strb.AppendFormat("{0} {1} matches: {2}\n", "Cert Hash", cert.GetCertHashString(), Enumerable.SequenceEqual<byte>(cer.GetCertHash(), cert.GetCertHash()));
    strb.AppendFormat("{0} matches: {2}\n", "Public Key", cert.GetPublicKeyString(), Enumerable.SequenceEqual<byte>(cer.GetPublicKey(), cert.GetPublicKey()));
    strb.AppendFormat("{0}: {1}, {2}", "Verification", cert.Verify(), cer2.Verify());

    var res = MessageBox.Show(strb.ToString(),
        "Allow certificate?", MessageBoxButtons.YesNo);
    return res == System.Windows.Forms.DialogResult.Yes;
});

con.Bind();

基本上,如果 VerifyServerCertificateCallback 返回 true,则连接成功,如果返回失败,则连接失败,出现与我尝试过的任何其他解决方案相同的异常。

奇怪的是,安装 AD 证书或 AD 控制器的根证书都没有帮助其他解决方案,但它确实改变了 Verify() 方法的结果。

我必须对回调中的证书执行哪些检查以维护 SSL 连接的安全性?

【问题讨论】:

  • 我也有同样的问题!你有没有设法解决这个问题?
  • @Markus 是的,我或多或少做到了。但由于我不再在那里工作,我将不得不凭记忆做出回应。本质上,事实证明我必须使用两个不同的 LDAP 库,具体取决于我是否需要 SSL。我无法让 LdapConnection 与 SSL 一起使用,并且较新的 .net 库拒绝在非 SSL 中工作。
  • 好的,“新的”.net 库是您上一个示例中的 LDAPConnection 吗?
  • @Markus:对。 LDAPConnection 是新的,DirectoryEntry 是旧的。

标签: c# ssl active-directory


【解决方案1】:

我建议你使用PrincipalContext 中的System.DirectoryServices.AccountManagement。初始化如下所示:

PrincipalContext context = new PrincipalContext(
            ContextType.Domain, NAME_OF_THE_DOMAIN + ":636", 
            null, 
            ContextOptions.SecureSocketLayer | ContextOptions.Negotiate, 
            this.textBoxUsername.Text, 
            this.textBoxPassword.Text);

之后您可以搜索UserPrincipal 及其DistinguishedName

string dn = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, this.textBoxUsername.Text).DistinguishedName;

如果您想遍历 AD 树,只需在 PrincipalSearcher 的帮助下执行类似操作:

using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
    foreach (var result in searcher.FindAll())
    {
        DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;

        //DO watherever you want
    }
}

【讨论】:

  • 我不是(只是)尝试查找帐户或组。我需要走入口广告树。我可以用这种方法做到这一点吗?
  • @Kempeth:当然——一旦你有一个通过 SSL 正确认证的PrincipalContext,你就可以用它做任何事情——查找用户、创建用户等等。
  • 我认为使用PrincipalContext/PrincipalSearcher 来检索DirectoryEntry 对象没有任何好处,如示例中所示...
  • 谢谢。但是当我尝试这样做时,我得到了一个 PrincipalServerDownException。
  • 也许你应该检查域名:例如:“Mydomain.local”。这不是服务器名称。
【解决方案2】:

Brian Desmond 快到了。 您需要在 DirectoryEntry AuthenticationType 中设置 2 个标志:

AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.安全

示例:

new DirectoryEntry("LDAP://" + this.textBoxServer.Text + ":636",
                    this.textBoxUsername.Text,
                    this.textBoxPassword.Text,
                    AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.Secure)

【讨论】:

  • +1 也为我确认 - AuthenticationTypes[flag]。需要同时设置SecureSocketsLayerSecure 才能正常工作。
【解决方案3】:

首先,您应该确保您的帐户和密码可用。使用ldp.exe工具检查账号是否可用。

那么也许你可以尝试如下,请使用 LDAP:// 而不是 LDAPS://

LdapConnection conn = new LdapConnection("xx1.bb.aa.com:636");
var op = conn.SessionOptions;
op.ProtocolVersion = 3;
op.SecureSocketLayer = true;
op.VerifyServerCertificate += delegate { return true; };

conn.AuthType = AuthType.Negotiate;
var cred = new NetworkCredential("accountname", "password");

//conn.Credential = cred;
try
{
    conn.Bind(cred);

    if (op.SecureSocketLayer)
    {
        Console.WriteLine("SSL for encryption is enabled - SSL information:");
    }
}
catch (Exception ex)
{

    throw;
}

或者:

DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://xx1.bb.aa.com:636",
    "ldapsusername", "password", AuthenticationTypes.SecureSocketsLayer);

//directoryEntry.Options

DirectorySearcher searcher = new DirectorySearcher(directoryEntry)
{
    PageSize = int.MaxValue,
    Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=hp.wang))"
};

searcher.PropertiesToLoad.Add("sn");

var result = searcher.FindOne();

if (result == null)
{
    return; // Or whatever you need to do in this case
}

string surname;

if (result.Properties.Contains("sn"))
{
    surname = result.Properties["sn"][0].ToString();
}

【讨论】:

  • 行得通!两个 cmets:1. 我不必设置 636 端口。只需使用常规的"LDAP://xx1.bb.aa.com" 2. 要授权为当前线程用户(在我的情况下为 App Pool 用户),请使用:new DirectoryEntry("LDAP://xx1.bb.aa.com", null, null, AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.Secure);
【解决方案4】:

我相信您需要做的就是调整这个 sn-p 以针对正确的端口(通常是 636):

new DirectoryEntry("LDAP://" + this.textBoxServer.Text + ":636",
                        this.textBoxUsername.Text,
                        this.textBoxPassword.Text,
                        AuthenticationTypes.SecureSocketsLayer)

【讨论】:

  • 如果尝试过但仍然出现“服务器无法运行”异常
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多