从System.DirectoryServices将StartTLS与LDAP一起使用

本文关键字:LDAP 一起 StartTLS System DirectoryServices | 更新日期: 2023-09-27 18:20:31

我正试图连接到一个需要StartTLS的LDAP服务器,但没有成功-每当我使用SessionOptions.StartTransportLayerSecurity(..)或将SessionOptions.SecureSocketLayer设置为true时,我都会遇到异常。

这是我正在使用的代码:

using (var connection = new LdapConnection(new LdapDirectoryIdentifier(config.LdapServer, config.Port, false, false)))
{
    connection.SessionOptions.ProtocolVersion = 3;
    connection.Credential = new NetworkCredential(config.BindDN, config.BindPassword);
    connection.SessionOptions.VerifyServerCertificate += (conn, cert) => {return true;};
    connection.AuthType = AuthType.Basic;
    //connection.SessionOptions.SecureSocketLayer = true;
    connection.SessionOptions.StartTransportLayerSecurity(null); // throws here, same if done after bind.
    connection.Bind();
    ... do stuff with connection
}

由此产生的异常为"TlsOperationException:发生未指定的错误",此错误发生在调用StartTransportLayerSecurity方法时。

我已经针对OpenLDAP服务器和Active Directory测试了代码,但两者都不起作用。

有人知道如何让StartTLS与System.DirectoryServices一起工作吗?

从System.DirectoryServices将StartTLS与LDAP一起使用

过去有相当多的LDAP堆栈不兼容,这仍然适用于客户可能使用的潜在遗留场景。

以下是关于OpenLDAP和Microsoft的LDAP堆栈之间不兼容的最常见问题(一旦有更多信息可用,我将修改和/或替换这些链接)

  • OpenLDAP StartTLS问题(ITS#3037)(总结于On geting OpenLDAP and Windows LDAP to interop)已触发相应的修补程序:
    • 无法将启动TLS请求从运行Windows Server 2003、Windows XP或Windows Vista的计算机发送到运行OpenLDAP软件的服务器
  • API通过LDAP服务发送到LDAP服务器的扩展操作会导致协议错误

显然,更新OpenLDAP和/或Windows(当然最好两者都更新)应该可以解决这些问题,如果它们是罪魁祸首的话。

祝你好运!

请阅读本主题:通过TLS/SSL加密连接进行绑定

例19。使用基本身份验证和SSL/TLS 绑定到安全端口50001上的ADAM实例

string hostNameAndSSLPort = "sea-dc-02.fabrikam.com:50001";
string userName = "cn=User1,cn=AdamUsers,cn=ap1,dc=fabrikam,dc=com";
string password = "adamPassword01!";
// establish a connection
LdapConnection connection = new LdapConnection(hostNameAndSSLPort);
// create an LdapSessionOptions object to configure session 
// settings on the connection.
LdapSessionOptions options = connection.SessionOptions;
options.ProtocolVersion = 3;
options.SecureSocketLayer = true;
connection.AuthType = AuthType.Basic;
NetworkCredential credential =
        new NetworkCredential(userName, password);
connection.Credential = credential;
try
{
    connection.Bind();
    Console.WriteLine("'nUser account {0} validated using " +
        "ssl.", userName);
    if (options.SecureSocketLayer == true)
    {
        Console.WriteLine("SSL for encryption is enabled'nSSL information:'n" +
        "'tcipher strength: {0}'n" +
        "'texchange strength: {1}'n" +
        "'tprotocol: {2}'n" +
        "'thash strength: {3}'n" +
        "'talgorithm: {4}'n",
        options.SslInformation.CipherStrength,
        options.SslInformation.ExchangeStrength,
        options.SslInformation.Protocol,
        options.SslInformation.HashStrength,
        options.SslInformation.AlgorithmIdentifier);
    }
}
catch (LdapException e)
{
    Console.WriteLine("'nCredential validation for User " +
        "account {0} using ssl failed'n" +
        "LdapException: {1}", userName, e.Message);
}
catch (DirectoryOperationException e)
{
    Console.WriteLine("'nCredential validation for User " +
    "account {0} using ssl failed'n" +
    "DirectoryOperationException: {1}", userName, e.Message);
}

下一个示例显示"如何使用TLS进行身份验证并执行任务"

string hostOrDomainName = "fabrikam.com";
string userName = "user1";
string password = "password1";
// establish a connection to the directory
LdapConnection connection = new LdapConnection(hostOrDomainName);
NetworkCredential credential =
    new NetworkCredential(userName, password, domainName);
connection.Credential = credential;
connection.AuthType = AuthType.Basic;
LdapSessionOptions options = connection.SessionOptions;
options.ProtocolVersion = 3;
try
{
    options.StartTransportLayerSecurity(null);
    Console.WriteLine("TLS started.'n");
}
catch (Exception e)
{
    Console.WriteLine("Start TLS failed with {0}", 
        e.Message);
    return;
}
try
{
    connection.Bind();
    Console.WriteLine("Bind succeeded using basic " +
        "authentication and SSL.'n");
    Console.WriteLine("Complete another task over " +
        "this SSL connection");
    TestTask(hostName);
}
catch (LdapException e)
{
    Console.WriteLine(e.Message);
}
try
{
    options.StopTransportLayerSecurity();
    Console.WriteLine("Stop TLS succeeded'n");
}
catch (Exception e)
{
    Console.WriteLine("Stop TLS failed with {0}", e.Message);
}
 Console.WriteLine("Switching to negotiate auth type");
 connection.AuthType = AuthType.Negotiate;
 Console.WriteLine("'nRe-binding to the directory");
 connection.Bind();
// complete some action over this non-SSL connection
// note, because Negotiate was used, the bind request 
// is secure. 
// run a task using this new binding
TestTask(hostName);

在这个问题上做了更多的工作后,我发现我遇到了几个问题:

  1. 代码中有一个错误,在连接到我们的测试套件(doh!)中的AD时,端口号被错误地更改为SSL端口(636)
  2. OpenLDAP测试服务器(它是我们客户的复制品)使用的是OpenLDAP-2.4.18,它存在已知的StartTLS问题

在将补丁应用到OpenLDAP之后(如这里所讨论的-http://www.openldap.org/lists/openldap-bugs/200405/msg00096.html)我们能够修复#2-在这一点上,我们开始得到一个不同的错误"发生了本地错误"。

虽然最初我们有这样的代码:

connection.SessionOptions.VerifyServerCertificate 
    += (conn, cert) => {return true;};

我们在测试时删除了它,因为OpenLDAP服务器使用的是自签名证书,所以它不在受信任的存储中。重新引入回调解决了这个问题,尽管我们现在将其作为一个可配置选项,即"验证服务器证书是/否",因此客户需要选择跳过检查(主要供我们的QA团队使用)。

感谢Steffen为我指明了OpenLDAP版本的方向,这使我找到了这个解决方案。