连接到 LDAPS(Active Directory)时出现 SSLHandshakeException

Posted

技术标签:

【中文标题】连接到 LDAPS(Active Directory)时出现 SSLHandshakeException【英文标题】:SSLHandshakeException while connection to LDAPS(Active Directory) 【发布时间】:2017-10-24 15:09:14 【问题描述】:

我们使用 LDAP(Active Directory) 来验证用户。 ldap 服务器是安全的,因此我们生成了证书并添加到我们的密钥库中。要生成和导入我们遵循的证书:

1.  Retrieve the public CA certificate from the server
echo | openssl s_client -connect <host>:<port> 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ca-cert.cer

2.  Import the certificate into the keystore.
sudo keytool -keystore <path-tojre>/jre/lib/security/cacerts -import -alias <name> -file ca-cert.crt

我们尝试使用以下 Java 程序对用户进行身份验证,但由于 SSL 握手异常而失败。如果我们尝试验证我们的 LDAP 服务帐户/主用户,它工作正常。下面是我们正在使用的代码。

import java.util.Hashtable;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class LdapAuthenticationTest 

private final static String mstrUsersearchBase = ",OU=ServiceAccounts,DC=org,DC=com";
private final static String userSearchBase = ",ou=Staff,DC=org,DC=com";
private static final String MASTER_USER = "MASTER_ID"+mstrUsersearchBase;
private static final String MASTER_PASSWORD = "MASTER_PASSWORD";
private final static String ldapUrl = "ldaps://server:636";

static String appUser = "userId";
static String appUserpassword = "password";

public static void main(String[] args) 
    String keystorePath = "/usr/lib/jvm/java-1.8.0-openjdk-1.8/jre/lib/security/cacerts";
    System.setProperty("javax.net.ssl.trustStore", keystorePath);
    // Password of your java keystore. Default value is : changeit
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

    DirContext ctx = null;
    try 
//          1. Authenticate using master user.
        ctx = authenticate();
        System.out.println("-------->>>  FOUND MASTER ACCOUNT!    <<<--------- ");


//          2. Authenticate using app user.
        ctx = authenticate(appUser, appUserpassword);
        System.out.println("-------->>>  FOUND USER ACCOUNT!    <<<--------");
        ctx.close();
     catch (AuthenticationException ae) 
        System.out.println(" Authentication failed - exception: " + ae);
     catch (Exception ie) 
        System.err.println("Unable to get LDAP connection ----->>>>> " + ie);
    


private static DirContext authenticate() throws NamingException 
    return authenticate(null, null);


private static DirContext authenticate(String username, String password) throws NamingException 
    String userType = username != null ? "App user" : "Master user";
    System.out.println("INSIDE Authenticate method:  authenticating-> "+ userType);

    String user = username != null ? username+userSearchBase : MASTER_USER;
    String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; 
    String securityAuthentication = "simple";

    Hashtable<String, String> env = new Hashtable<>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
    env.put(Context.SECURITY_AUTHENTICATION, securityAuthentication);
    env.put(Context.PROVIDER_URL, ldapUrl);
    String principal = "CN=" + user;
    System.out.println("principal-> "+principal);
    env.put(Context.SECURITY_PRINCIPAL, principal);
    env.put(Context.SECURITY_CREDENTIALS, password != null ? password : MASTER_PASSWORD);
    // Specify SSL
    env.put(Context.SECURITY_PROTOCOL, "ssl");
    DirContext ctx = new InitialDirContext(env);

    return ctx;



我在控制台中的登录为:

-------->>>  FOUND MASTER ACCOUNT!    <<<---------

后跟以下异常:

javax.naming.CommunicationException: simple bind failed: server:636 [Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]

Java 应用程序和 LDAP 服务器都在不同的机器上(Linux 环境)

【问题讨论】:

检查您的 Java 应用是否有权访问颁发 LDAPS 证书的根 CA。 您的意思是使用 TrustManager 编写程序并读取所有证书?我会看到一个示例代码,因为我不知道如何使用它。同时,我使用 Debug=ALL 选项运行应用程序。我看到为证书写的无效,原因是(未知证书),然后是 closeSocket() 调用。在此 SSLHandshake 异常之后。我怀疑它是否喜欢读取密钥库并在发现其中一个证书无效时关闭套接字。 不,我的意思是检查颁发 LDAPS 证书的 CA 的根 CA 证书(或证书本身,如果它是自签名的)是否存在于 Java 的证书存储中,而 Java 的证书存储又存在于应用程序中验证证书。如果没有,让它存在。应该可以通过运行您的应用程序的 PC 上的 Java 控制面板访问这些内容。 现在可以使用了。以上更新。 最好将更新发布为解决方案,这样问题将被列为“已回答”,并且可能不会被正在搜索相同问题的人过滤掉以得到零答案。 【参考方案1】:

[已解决]

能够解决问题。实际上就在 SSLHandshake 错误和 closeSocket() 调用之前,我有这个:

%% Invalidated:  [Session-2, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
main, SEND TLSv1.2 ALERT:  fatal, description = certificate_unknown

了解到 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 是服务器所期望的密码。由于某些国家/地区的限制,Java-8 的密码有限。细节 here

Oracle 单独提供 UnlimitedPolicy jar 用于额外的密码,可以从 PolicyJars 下载

我不得不替换 jre/lib/security 文件夹中的 jar,并且不得不再次重新安装证书。之后一切正常。

【讨论】:

以上是关于连接到 LDAPS(Active Directory)时出现 SSLHandshakeException的主要内容,如果未能解决你的问题,请参考以下文章

无法从 python-ldap 连接到 Windows Server 2016 上的 ldaps

受信任域之间的 Active Directory LDAPS

通过 LDAP 连接到 Active Directory

将本地设备连接到 Azure Active Directory 域服务

使用 Active Directory 服务帐户从 .NET 连接到 SQL Server

使用 DataGrip - Active Directory 连接到 Azure 数据库