2011-01-06 14 views
10

Necesito conectarme a un directorio LDAP sobre SSL.cómo aceptar certificados autofirmados para conexiones JNDI/LDAP?

En entornos no productivos, utilizamos los certificados de firma propia que, por supuesto, no puede validar con:

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) 

soy consciente de cómo utilizar un custom trust manager for SSL-enabled connections, pero no sabemos cómo use uno en conexión con la API JNDI donde no administro la conexión real. Es decir, ¿dónde está la siguiente configuración estándar para poder conectar el administrador de confianza?

Gracias de antemano.

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 (...) 

Respuesta

18

De acuerdo con la documentación de JNDI parece posible establecer una costumbre 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 */ 
    ... 
} 

Configurar el entorno para utilizar esta fábrica de sockets

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

Gracias. Creo que es lo correcto; Tengo algunas dificultades para ejecutarlo dentro de JBoss, sospecho que los diferentes cargadores de clases causan una "java.lang.IllegalArgumentException: el objeto no es una instancia de declaración de clase" cuando el socket se instancia a través de la reflexión en com.sun.jndi.ldap.Connection.createSocket (Connection.java:317) pero ese es un problema diferente – wishihadabettername

+9

Solo para ayudar a alguien a encontrarse con la IllegalArugumentException; necesita implementar el método estático getDefault() en su SocketFactory personalizado (SSL). –

1

Han pasado unos 5 años desde la última vez que hice esto, por lo que mi respuesta será un poco vaga, me temo; pero creo que si implementas un proveedor de confianza en java.security que deberías encontrar en/usr/java/jre/lib/security /; luego aceptará el certificado como confiable.

no tengo acceso a mis notas en este momento, pero voy a tener una excavación a través de ellos más adelante

6

Se podría aceptar cualquier certificado cuando se reemplaza la 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

Al hacerlo, no solo está aceptando este certificado autofirmado, está desactivando por completo la verificación de confianza. Por lo tanto, también está aceptando cualquier certificado, incluyendo potencialmente el de un MITM atacando su conexión. – Bruno

+0

Trabajado ¡Me gustó la mantequilla, gracias! – Sohan

2

La respuesta de @Jcs es correcta, pero si no desea utilizar un TrustManager personalizado, y si desea que ese certificado autofirmado sea una CA confiable para otros usos dentro de la VM, usted puede:

  • agregar este certificado en el almacén de confianza predeterminado (por lo general cacerts bajo el directorio de seguridad JRE) o
  • crear un nuevo almacén de confianza en una ubicación diferente (posiblemente basado en una copia del defecto cacerts), que haría contiene ese certificado en particular, y úselo como su almacén de confianza predeterminado, pero configurando las propiedades del sistema javax.net.ssl.trustStore* .
1

Dejando a un lado por completo JNDI, algunos marcos se refieren a la configuración de LDAP sistema local (que suele ser /etc/ldap.conf o algo por el estilo). Cuando digo LOCAL, me refiero a los sistemas en los que se ejecuta su operación JNDI.

Al menos en el caso de la mayoría LDAP Linux OS, dentro de un archivo de este tipo de configuración LDAP es (generalmente) una línea que dice:

TLS_REQCERT demand 

Ésta es la configuración TLS criticidad default/SSL (y el más estricto), ya que fallará la conexión si CUALQUIER COSA está mal con el certificado (esto incluye ser autofirmado).

Puede experimentar con diferentes configuraciones en lugar de 'demandar' (jugar con 'permitir' o 'nunca'). Una vez hecho esto, intente de nuevo su operación y vea si su problema desaparece. Nuevamente, no todas las cosas como esta leerán o incluso detectarán la configuración de su sistema local. Algunos lo hacen, otros no. Digno de una mirada.

espero que esto ayude ...

Max

4

No se requiere código. Simplemente agregue el certificado a los almacenes de confianza del cliente de prueba.

No escriba código adicional para esto. No es seguro y corres un gran riesgo de que el código se filtre a la producción. Realmente no quieres eso.

Cuestiones relacionadas