SSL 和自定义套接字

除了 SASL 身份验证之外,大多数 LDAP 服务器都允许通过 SSL 访问其服务。 SSL 对于 LDAP v2 服务器特别有用,因为 v2 协议不支持 SASL 身份验证。

启用 SSL 的服务器通常以两种方式支持 SSL。以最基本的方式,服务器除了支持正常(不受保护)端口外,还支持 SSL 端口。服务器支持 SSL 的另一种方式是使用“启动 TLS 扩展”(RFC 2830)。此选项仅适用于 LDAP v3 服务器,本节对此有详细说明。

使用 SSL 套接字属性

默认情况下,与 LDAP 服务器通信时,JDK 中的 LDAP 服务提供者使用普通套接字。要请求使用 SSL 套接字,请将Context.SECURITY_PROTOCOL属性设置为"ssl"

following example中,LDAP 服务器在端口 636 处提供 SSL。要运行此程序,必须在 LDAP 服务器的端口 636 上启用 SSL。此过程通常由目录的 管理 员执行。


服务器要求: LDAP 服务器必须使用 X.509 SSL 服务器证书设置并启用 SSL。通常,您必须首先从证书颁发机构(CA)获得服务器的签名证书。然后,按照您的目录供应商提供的有关如何启用 SSL 的说明进行操作。不同的供应商有不同的工具来执行此操作。

对于Oracle 目录服务器 v5.2,使用 管理 控制台中的“管理 证书”工具来生成证书签名请求(CSR)。将 CSR 提交给 CA 以获取 X.509 SSL 服务器证书。使用 管理 控制台,将证书添加到服务器的证书列表中。如果服务器的受信任 CA 列表中还没有 CA 证书,也请安装该证书。通过使用 管理 控制台中的“配置”选项卡启用 SSL。在左窗格中选择服务器。在右窗格中选择“加密”选项卡。单击“为此服务器启用 SSL”和“使用此密码家族:RSA”复选框,确保已添加的服务器证书在证书列表中。

Client 端要求: 您需要确保 Client 端信任您将使用的 LDAP 服务器。您必须在 JRE 的可信证书数据库中安装服务器的证书(或其 CA 的证书)。这是一个例子。

# cd JAVA_HOME/lib/security
# keytool -import -file server_cert.cer -keystore jssecacerts

有关如何使用安全工具的信息,请参见Security。有关 JSSE 的信息,请参见Java 安全套接字扩展(JSSE)参考指南


// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:636/o=JNDITutorial");

// Specify SSL
env.put(Context.SECURITY_PROTOCOL, "ssl");

// Authenticate as S. User and password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires,o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

// ... do something useful with ctx

注意: 如果您使用 SSL 连接到使用 SSL 的端口上的服务器,则程序将挂起。同样,如果您使用普通套接字连接到服务器的 SSL 套接字,则应用程序将挂起。这是 SSL 协议的 Feature。


使用 LDAPS URL

除了通过使用Context.SECURITY_PROTOCOL属性来请求使用 SSL 之外,您还可以通过使用 LDAPS URL 来请求使用 SSL。 LDAPS URL 与 LDAP URL 相似,不同之处在于 URL 方案是“ ldaps”而不是“ ldap”。与 LDAP 服务器通信时,它指定使用 SSL。

following example中,LDAP 服务器在端口 636 处提供 SSL。要运行此程序,必须在 LDAP 服务器的端口 636 上启用 SSL。

// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Specify LDAPS URL
env.put(Context.PROVIDER_URL, "ldaps://localhost:636/o=JNDITutorial");

// Authenticate as S. User and password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, 
        "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

// ... do something useful with ctx

在任何接受 LDAP URL 的地方都接受 LDAPS URL。请查看JNDI Tutorial以获取有关 LDAP 和 LDAPS URL 的详细信息。

Client 端身份验证:将 SSL 与外部 SASL 机制配合使用

SSL 在比 LDAP 更低的层上提供身份验证和其他安全服务。如果已经在 SSL 上完成了身份验证,则 LDAP 层可以通过使用External SASL 机制来使用来自 SSL 的身份验证信息。

following example类似于先前的 SSL 示例,除了使用简单的身份验证,它使用外部 SASL 身份验证。通过使用外部,您不需要提供任何主体或密码信息,因为它们是从 SSL 中获取的。


服务器要求: 本示例要求 LDAP 服务器允许基于证书的 Client 端身份验证。另外,LDAP 服务器必须信任它所接收的 Client 端证书(的 CA),并且必须能够将 Client 端证书中的所有者专有名称 Map 到它所知道的主体。按照目录供应商的说明执行有关这些任务的操作。

Client 端要求: 本示例要求 Client 端具有 X.509 SSLClient 端证书。此外,证书必须作为第一个密钥条目存储在密钥库文件中。如果此条目受密码保护,则它必须具有与密钥库相同的密码。有关 JSSE 密钥库的更多信息,请参见Java 安全套接字扩展(JSSE)参考指南


// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:636/o=JNDITutorial");

// Principal and credentials will be obtained from the connection
env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL");

// Specify SSL
env.put(Context.SECURITY_PROTOCOL, "ssl");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

...

要运行此程序,以便使用 Client 机的证书进行身份验证,必须(作为系统属性)提供包含 Client 机的证书的密钥库的位置和密码。这是如何运行程序的示例。

java -Djavax.net.ssl.keyStore=MyKeystoreFile \
    -Djavax.net.ssl.keyStorePassword=mysecret \
    External

如果不提供密钥库,则该程序将使用匿名身份验证运行,因为 SSL 上不存在任何 Client 端凭据。

此示例显示完成基于证书的 Client 端身份验证的最基本方法。可以通过编写和使用自定义套接字工厂来实现更高级的方法,该工厂可以更灵活的方式访问 Client 端证书,也许使用 LDAP 目录。下一节将说明如何在 JNDI 应用程序中使用自定义套接字工厂。

使用自定义套接字

使用 SSL 时,默认情况下,LDAP 提供程序将使用套接字工厂javax.net.ssl.SSLSocketFactory来创建 SSL 套接字,以使用默认的 JSSE 配置与服务器进行通信。可以通过多种方式自定义 JSSE,如Java 安全套接字扩展(JSSE)参考指南中所述。但是,有时这些自定义功能还不够,您需要对 LDAP 服务提供者使用的 SSL 套接字(或通常的套接字)有更多的控制权。例如,您可能需要可以绕过防火墙的套接字,或者使用对其信任和密钥存储使用非默认缓存/检索策略的 JSSE 套接字。要设置 LDAP 服务提供者使用的套接字工厂实现,请将"java\.naming\.ldap\.factory\.socket"属性设置为套接字工厂的标准类名。此类必须实现javax.net.SocketFactory抽象类,并提供getDefault()方法的实现,该方法返回套接字工厂的实例。参见Java 安全套接字扩展(JSSE)参考指南

这是产生普通套接字的定制 socket 厂的示例。

public class CustomSocketFactory extends SocketFactory {
    public static SocketFactory getDefault() {

        System.out.println("[acquiring the default socket factory]");
        return new CustomSocketFactory();
    }
        ...
}

请注意,此示例在每次创建新的 LDAP 连接时都会创建一个新的CustomSocketFactory实例。这可能适用于某些应用程序和套接字工厂。如果要重用同一套接字工厂,则getDefault\(\)应该返回一个单例。

要将此自定义套接字工厂与 JNDI 程序一起使用,请设置"java\.naming\.ldap\.factory\.socket"属性,如following example所示。

// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

// Specify the socket factory
env.put("java.naming.ldap.factory.socket", "CustomSocketFactory");

// Create the initial context
DirContext ctx = new InitialDirContext(env);

// ... do something useful with ctx

"java\.naming\.ldap\.factory\.socket"属性可用于根据每个上下文设置套接字工厂。控制 LDAP 服务提供者使用的套接字的另一种方法是使用java.net.Socket.setSocketImplFactory()为整个程序中使用的所有套接字设置套接字工厂。使用此方法的灵 Active 较差,因为它会影响所有套接字连接,而不仅是 LDAP 连接,因此应谨慎使用。