Ste*_*eil 3 java ssl jndi ldap jsse
我们正在编写一个应连接到不同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() );
}
Run Code Online (Sandbox Code Playgroud)
我们可以添加自己的CustomSocketFactory,但是如何传递信息呢?
对于其他人也有同样的问题:我为我的案件找到了一个非常丑陋的解决方案:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
env.put( "java.naming.ldap.factory.socket", ThreadLocalSocketFactory.class.getName() );
ThreadLocalSocketFactory.set( sslContext.getSocketFactory() );
try {
ctx = new InitialLdapContext( env, null );
} finally {
ThreadLocalSocketFactory.remove();
}
Run Code Online (Sandbox Code Playgroud)
不好,但是行得通。JNDI应该在这里更加灵活...