2011-01-06 4 views
10

Мне нужно подключиться к каталогу LDAP через SSL.как принять самоподписанные сертификаты для соединений JNDI/LDAP?

В непроизводственных условиях, мы используем собственные сертификаты, которые, конечно, не в состоянии проверить с:

javax.naming.CommunicationException: simple bind failed: ldapserver: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] 
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:197) 
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2694) 
at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:293) 
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175) 
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193) 
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136) 
at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66) 
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667) 
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288) 
at javax.naming.InitialContext.init(InitialContext.java:223) 
at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:134) 

Я знаю о том, как использовать custom trust manager for SSL-enabled connections, но не знаю, как используйте один в связи с JNDI API, где я не управляю фактическим подключением. То есть, где следующая стандартная настройка, я смогу подключить диспетчер доверия?

Заранее спасибо.

Hashtable env = new Hashtable(); 
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
env.put(Context.PROVIDER_URL, "ldaps://ldapserver:636"); 
env.put(Context.SECURITY_PROTOCOL, "ssl"); 
env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
env.put(Context.SECURITY_PRINCIPAL, "myUser"); 
env.put(Context.SECURITY_CREDENTIALS, "myPassword"); 
LdapContext ctx = new InitialLdapContext(env, null); 
ctx.search (...) 

ответ

18

Согласно документации JNDI представляется возможным установить пользовательский SSLSocketFactory

http://download.oracle.com/javase/1.5.0/docs/guide/jndi/jndi-ldap-gl.html#socket

public class MySSLSocketFactory extends SocketFactory { 
    private SSLSocketFactory sf; 

    public MySSLSocketFactory() { 
     KeyStore keyStore = ... /* Get a keystore containing the self-signed certificate) */ 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(); 
     tmf.init(keyStore); 
     SSLContext ctx = SSLContext.getInstance("TLS"); 
     ctx.init(null, tmf.getTrustManagers(), null); 
     sf = ctx.getSocketFactory(); 
    } 

    /* delegate SSLSocketFactory public methods to sf */ 
    ... 
} 

Настройка среды для использования этого гнезда завод

env.put("java.naming.ldap.factory.socket", "com.example.MySSLSocketFactory"); 
+0

Спасибо. Я думаю, что это правильно; У меня есть некоторые трудности с запуском внутри JBoss, я подозреваю, что разные загрузчики классов вызывают «объект java.lang.IllegalArgumentException: не является экземпляром объявления класса», когда сокет создается через отражение в com.sun.jndi.ldap.Connection.createSocket (Connection.java:317) , но это другая проблема – wishihadabettername

+9

Просто чтобы помочь кому-то запунуться в IllegalArugumentException; вам необходимо реализовать статический метод getDefault() в пользовательском (SSL) SocketFactory. –

1

Его было около 5 лет с тех пор, как я последний раз это делал, поэтому мой ответ будет немного расплывчатым, я боюсь; но я думаю, что если вы введете доверенный поставщик в java.security, который вы должны найти в/usr/java/jre/lib/security /; он затем примет сертификат как доверенный.

Я не имею доступ к моим записям в данный момент, но я буду рыть через них позже

6

Вы можете принять любой сертификат, если переопределить TrustManager:

DummyTrustmanager.java

public class DummyTrustmanager implements X509TrustManager { 
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { 
     // do nothing 
    } 

    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { 
     // do nothing 
    } 

    public X509Certificate[] getAcceptedIssuers() { 
     return new java.security.cert.X509Certificate[0]; 
    } 
} 

MySSLSocketFactory.java

public class MySSLSocketFactory extends SSLSocketFactory { 
    private SSLSocketFactory socketFactory; 

    public MySSLSocketFactory() { 
     try { 
      SSLContext ctx = SSLContext.getInstance("TLS"); 
      ctx.init(null, new TrustManager[] { new DummyTrustmanager() }, new SecureRandom()); 
      socketFactory = ctx.getSocketFactory(); 
     } catch (Exception ex) { 
      ex.printStackTrace(System.err); 
      /* handle exception */ 
     } 
    } 

    public static SocketFactory getDefault() { 
     return new MySSLSocketFactory(); 
    } 

    @Override 
    public String[] getDefaultCipherSuites() { 
     return socketFactory.getDefaultCipherSuites(); 
    } 

    @Override 
    public String[] getSupportedCipherSuites() { 
     return socketFactory.getSupportedCipherSuites(); 
    } 

    @Override 
    public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { 
     return socketFactory.createSocket(socket, string, i, bln); 
    } 

    @Override 
    public Socket createSocket(String string, int i) throws IOException, UnknownHostException { 
     return socketFactory.createSocket(string, i); 
    } 

    @Override 
    public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { 
     return socketFactory.createSocket(string, i, ia, i1); 
    } 

    @Override 
    public Socket createSocket(InetAddress ia, int i) throws IOException { 
     return socketFactory.createSocket(ia, i); 
    } 

    @Override 
    public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { 
     return socketFactory.createSocket(ia, i, ia1, i1); 
    } 
} 

Main.java

public class Main { 
    public static void main(String[] args) throws NamingException { 
     Hashtable env = new Hashtable(11); 
     env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
     env.put(Context.PROVIDER_URL, "ldaps://ldapserver:636/"); 
     env.put(Context.SECURITY_PROTOCOL, "ssl"); 
     env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
     env.put(Context.SECURITY_PRINCIPAL, "myUser"); 
     env.put(Context.SECURITY_CREDENTIALS, "myPassword"); 
     env.put("java.naming.ldap.factory.socket", "ldapsecure.MySSLSocketFactory"); 
     LdapContext ctx = new InitialLdapContext(env, null); 
    } 
} 
+6

Поступая таким образом, вы не просто принимаете этот самозаверяющий сертификат, вы полностью отключите проверку доверия. Таким образом, вы также принимаете любой сертификат, потенциально включающий в себя MITM, атакующий ваше соединение. – Bruno

+0

Работал Любимое масло, спасибо! – Sohan

2

@Jcs ответ правильный, но если вы не хотите использовать настраиваемый TrustManager, и если вы хотите, чтобы этот отдельный самозаверяющий сертификат был доверенным ЦС для других целей в виртуальной машине, вы может либо:

  • добавить этот сертификат к вашим доверенным сертификатам по умолчанию (обычно cacerts в директории безопасности JRE) или
  • создать новые доверенные сертификаты в другом месте (возможно, на основании копии по умолчанию cacerts), который будет содержат этот конкретный сертификат и использовать его в качестве своего доверия по умолчанию, но устанавливая свойства системы javax.net.ssl.trustStore* ,
1

Откладывая JNDI полностью, некоторых рамок будут относиться к конфигурации локальной системы LDAP (как правило, это /etc/ldap.conf или что-то в этом роде). Когда я говорю LOCAL, я имею в виду систему (ы), на которой выполняется ваша JNDI-операция.

По крайней мере, в случае большинства LDAP с поддержкой Linux операционных систем, внутри такого файла LDAP конфигурации (обычно) линия, которая гласит:

TLS_REQCERT demand 

Это по умолчанию TLS/SSL установка по критичности (и самый строгий), так как это приведет к сбою соединения, если что-то не так с сертификатом (это включает в себя самоподписание).

Вы можете экспериментировать с различными настройками вместо «спроса» (играть с «разрешить» или «никогда»). Как только это будет сделано, повторите попытку и проверьте, не исчезла ли ваша проблема. Опять же, не все такие вещи читают или даже обнаруживают ваши локальные системные настройки. Некоторые это делают, другие - нет. Стоит посмотреть.

Я надеюсь, что это помогает ...

Max

4

не требуется код. Просто добавьте сертификат в тестовые клиентские магазины.

Не используйте для этого дополнительный код. Это небезопасно, и вы подвергаетесь серьезному риску проникновения кода в производство. Вы действительно этого не хотите.