我们正在编写一个应用程序,它将连接到不同的LDAP服务器。对于每台服务器,我们只接受某个证书。该证书中的主机名无关紧要。当我们使用LDAP和STARTTLS时,这很容易,因为我们可以使用StartTlsResponse.setHostnameVerifier(..-)
并使用StartTlsResponse.negotiate(...)
匹配SSLSocketFactory
。但是,我们还需要支持LDAPS连接。 Java本身支持此功能,但前提是默认的Java密钥库信任服务器证书。虽然我们可以替换它,但我们仍然无法为不同的服务器使用不同的密钥库。
现有的连接代码如下:
Hashtable<String,String> env = new Hashtable<String,String>();
env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( Context.PROVIDER_URL, ( encryption == SSL ? "ldaps://" : "ldap://" ) + host + ":" + port );
if ( encryption == SSL ) {
// env.put( "java.naming.ldap.factory.socket", "CustomSocketFactory" );
}
ctx = new InitialLdapContext( env, null );
if ( encryption != START_TLS )
tls = null;
else {
tls = (StartTlsResponse) ctx.extendedOperation( new StartTlsRequest() );
tls.setHostnameVerifier( hostnameVerifier );
tls.negotiate( sslContext.getSocketFactory() );
}
我们可以添加自己的CustomSocketFactory
,但是如何将信息传递给它?
答案 0 :(得分:5)
对于其他人有同样的问题:我找到了一个非常难看的解决方案:
import javax.net.SocketFactory;
public abstract class ThreadLocalSocketFactory
extends SocketFactory
{
static ThreadLocal<SocketFactory> local = new ThreadLocal<SocketFactory>();
public static SocketFactory getDefault()
{
SocketFactory result = local.get();
if ( result == null )
throw new IllegalStateException();
return result;
}
public static void set( SocketFactory factory )
{
local.set( factory );
}
public static void remove()
{
local.remove();
}
}
像这样使用它:
env.put( "java.naming.ldap.factory.socket", ThreadLocalSocketFactory.class.getName() );
ThreadLocalSocketFactory.set( sslContext.getSocketFactory() );
try {
ctx = new InitialLdapContext( env, null );
} finally {
ThreadLocalSocketFactory.remove();
}
不好,但确实有效。 JNDI在这里应该更加灵活......
答案 1 :(得分:2)
您应该传递自己的SSLSocketFactory
子类的名称,并将其完全限定的名称传递到"java.naming.ldap.factory.socket"
env属性,如"Using Custom Sockets" section of the Java LDAP/SSL guide中所述:
env.put("java.naming.ldap.factory.socket", "example.CustomSocketFactory");
您无法将任何特定参数传递给此类,请参阅com.sun.jndi.ldap.Connection.createSocket(...)
中的实例化:
Class socketFactoryClass = Obj.helper.loadClass(socketFactory);
Method getDefault =
socketFactoryClass.getMethod("getDefault", new Class[]{});
Object factory = getDefault.invoke(null, new Object[]{});
如果您需要其他参数,可能必须使用静态成员或JNDI(通常不理想)。
据我所知,遗憾的是,在此实现中使用ldaps://
时似乎没有任何主机名验证。如果您只信任信任管理器中的一个显式证书,那么无论如何都应该补偿主机名验证的缺失。