jndi LDAPS пользовательские HostnameVerifier и TrustManager - PullRequest
3 голосов
/ 22 февраля 2012

Мы пишем приложение, которое будет подключаться к различным серверам 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, но как передать информацию?

Ответы [ 2 ]

5 голосов
/ 15 марта 2012

У других такая же проблема: я нашел очень уродливое решение для моего дела:

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 должен быть более гибким ...

2 голосов
/ 22 февраля 2012

Вы должны передать имя собственного подкласса SSLSocketFactory и передать его полностью определенное имя в свойство "java.naming.ldap.factory.socket" env, как описано в разделе " Использование пользовательских сокетов " JavaРуководство по LDAP / SSL :

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 (обычно не идеальный).

Насколько я могу судить, при использовании * 1018, похоже, не выполняется проверка имени хоста.* в этой реализации, к сожалению.Если вы доверяете только одному явному сертификату в вашем диспетчере доверия, это должно компенсировать отсутствие проверки имени хоста.

...