2010-05-10 13 views
7

Necesita orientación.Java Webstart Truststore SSL

Tengo la aplicación java webstart y quiero que se conecte a un servidor a través de SSL. Simplemente agregue una propiedad como: System.setProperty ("javax.net.ssl.trustStore", "my.keystore"); Pero desde un programa JAWS descargado del servidor no funciona y no tiene un my.keystore en el sistema de archivos local. Así que decidí distribuir el certificado a todos los clientes. Hice lo siguiente y funcionó.

  1. Lea esta tienda de confianza como una secuencia (utilice el método getResourceAsStream).
  2. Guárdelo en cualquier archivo en la máquina del cliente (sometemp)
  3. Llame a System.setProperty ("javax.net.ssl.trustStore", trustStorePath);

Pero estoy seguro de que debe haber mejores soluciones que esta ... ¿Alguna idea para mejorarla?

public boolean validateUserFromActiveDirectory(String userId) { 
        final String MEMBER_GROUP = "CN=asdadasd,OU=asdasdasd Accounts,OU=adasdas,OU=asdasdas,DC=asdasdas,DC=asdasdas,DC=adasdasd,DC=asdasdasd"; 
      String employeeNumber = ""; 
      final String LDAP_INIT_CTX = "com.sun.jndi.ldap.LdapCtxFactory"; 
      final String LDAP_URL = "ldap://xx-ssssssss.eee.eee.eeeee.eeeee:636"; 
      final String MY_ATTRS[] = { "employeeNumber" }; 
      String adminPassword = "somepassword"; 
      String securityProtocol = "ssl"; 
      boolean isValidUser = false; 
      try { 

        Hashtable env = new Hashtable(); 
        env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_INIT_CTX); 
        env.put(Context.PROVIDER_URL, LDAP_URL); 
        env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
        env.put(Context.REFERRAL, "follow"); 
        env.put(Context.SECURITY_PRINCIPAL, MEMBER_GROUP); 
        env.put(Context.SECURITY_CREDENTIALS, adminPassword); 
        env.put(Context.SECURITY_PROTOCOL, securityProtocol); 

      //C:\Documents and Settings\yourusername\Local Settings\Temp 
      File tf = File.createTempFile("someTruststore", ".jks"); 
      tf.deleteOnExit(); 
      byte buffer[] = new byte[0x1000]; 
       ClassLoader cl = JNDI.class.getClassLoader(); 
      InputStream in = cl.getResourceAsStream(
        "someTruststore.jks"); 
      FileOutputStream out = new FileOutputStream(tf); 
      int cnt; 
      while ((cnt = in.read(buffer)) != -1) 
       out.write(buffer, 0, cnt); 
      in.close(); 
      out.close(); 
      System.setProperty("javax.net.ssl.trustStore", tf 
          .getAbsolutePath()); 

        DirContext context = new InitialLdapContext(env, null); 
        SearchControls searchControls = new SearchControls(); 
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
        NamingEnumeration results = context.search(
           "XX=ent,XX=abc,XX=aaaaa,XX=aaaa", "(sAMAccountName=" 
              + userId + ")", searchControls); 

        if (results != null && results.hasMore()) { 
         //some logic 

         } 
        } 
      } catch (Exception e) { 
        e.printStackTrace(); 
      } 
      return isValidUser; 
     } 

-Padur =========================== ** =========== ==

/** 

* */

package util; 

/** 
* @author spaduri 
* 
*/ 
import java.io.IOException; 
import java.net.InetAddress; 
import java.net.Socket; 

import javax.net.SocketFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLSocketFactory; 
import javax.net.ssl.TrustManager; 

public class CustomSSLSocketFactory extends SSLSocketFactory { 

    private SSLSocketFactory factory; 

    public CustomSSLSocketFactory() { 
     try { 
      SSLContext sslcontext = null; 
       // Call getKeyManagers to get suitable key managers 
      KeyManager[] kms=getKeyManagers(); 
      if (sslcontext == null) { 
       sslcontext = SSLContext.getInstance("SSL"); 
       sslcontext.init(kms, 
       new TrustManager[] { new CustomTrustManager() }, 
       new java.security.SecureRandom()); 
      } 
      factory = (SSLSocketFactory) sslcontext.getSocketFactory(); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 


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

    public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException { 
     return factory.createSocket(socket, s, i, flag); 
    } 

    public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException { 
     return factory.createSocket(inaddr, i, inaddr1, j); 
    } 

    public Socket createSocket(InetAddress inaddr, int i) throws IOException { 
     return factory.createSocket(inaddr, i); 
    } 

    public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException { 
     return factory.createSocket(s, i, inaddr, j); 
    } 

    public Socket createSocket(String s, int i) throws IOException { 
     return factory.createSocket(s, i); 
    } 

    public String[] getDefaultCipherSuites() { 
     return factory.getSupportedCipherSuites(); 
    } 

    public String[] getSupportedCipherSuites() { 
     return factory.getSupportedCipherSuites(); 
    } 

protected KeyManager[] getKeyManagers() 
     throws IOException, GeneralSecurityException 
     { 
     // First, get the default KeyManagerFactory. 
     String alg=KeyManagerFactory.getDefaultAlgorithm(); 
     KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg); 

     // Next, set up the KeyStore to use. We need to load the file into 
     // a KeyStore instance. 

     ClassLoader cl = CustomSSLSocketFactory.class.getClassLoader(); 
     // read the file someTrustStore from the jar file from a classpath 
     InputStream in = cl.getResourceAsStream("ssl/someTruststore.jks"); 
     //FileInputStream fis=new FileInputStream(adentTruststore.jks); 
     KeyStore ks=KeyStore.getInstance("jks"); 
     ks.load(in, null); 
     in.close(); 

     // Now we initialise the KeyManagerFactory with this KeyStore 
     kmFact.init(ks, null); 

     // And now get the KeyManagers 
     KeyManager[] kms=kmFact.getKeyManagers(); 
     return kms; 
     } 
} 

package util; 
import java.security.cert.X509Certificate; 

import javax.net.ssl.X509TrustManager; 

public class CustomTrustManager implements X509TrustManager { 

    public void checkClientTrusted(X509Certificate[] cert, String authType) { 
     return; 
    } 

    public void checkServerTrusted(X509Certificate[] cert, String authType) { 
     return; 
    } 

    public X509Certificate[] getAcceptedIssuers() { 
     return new X509Certificate[0]; 
    } 
} 

Laz apreciar su paciencia, tratando de aprender cuando toma algo de tiempo Empecé a escribir mi propia CustomSSLSocketFactory ... ahora estoy pasando por alto la seguridad ... basada en el ejemplo de las soluciones platinum. Si lo hago ... ¿pasará la información como texto claro en la red?

Ahora me pregunto qué debo hacer con el archivo truststore Tengo el archivo "sometruststore.jks". ¿Qué debo hacer con eso? ¿Tengo mi propio software personalizado de trustmanager? Por favor, guíame en la dirección correcta.

-padur

+0

La información no será texto claro. Se cifrará, pero no se autenticará, ya que este código considera que todos los certificados son confiables. No es necesario que escriba su propio trustmanager para manejar el archivo .jks. Mire mi respuesta a continuación y vea que puede pasar una instancia de KeyStore a la subclase SSLSocketFactory. Puede obtener esa instancia de la misma manera que está en su código original cargando si está fuera de la ruta de clases. – laz

Respuesta

3

Usted puede hacerlo sin tener que depender de las propiedades del sistema y el sistema de archivos. Leer el almacén de claves como una corriente como la que está haciendo y crear su propia SSLSocketFactory sería mucho más limpio.

import java.net.URL; 
import java.security.KeyStore; 
import java.security.SecureRandom; 

import javax.net.ssl.HttpsURLConnection; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.TrustManagerFactory; 

... 

    // assume keyStore is the KeyStore you read via getResourceAsStream 
    final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); 
    trustManagerFactory.init(keyStore); 

    final SSLContext context = SSLContext.getInstance("SSL"); 
    context.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); 

    final URL url = new URL("https://whatever"); 
    final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); 
    urlConnection.setSSLSocketFactory(context.getSocketFactory()); 

... 

No he verificado pero no veo ninguna razón por la que esto no debería funcionar a través de Webstart.

Actualizado:

usted menciona que usted está buscando para conectar con el directorio activo, así que supongo que se va a utilizar LDAPS como el protocolo? Si es así, ¿quizás el código en this URL puede servir como inspiración? Deberá crear una subclase de javax.net.ssl.SSLSocketFactory (consulte BlindSSLSocketFactoryTest en ese enlace platinumsolutions) que ajusta la lógica anterior para crear el SSLContext y delega llamadas al SSLSocketFactory que crea context.getSocketFactory().

public class TrustedSSLSocketFactory extends SSLSocketFactory { 
    private static SSLContext context; 
    public static void initTrustedSSLSocketFactory(final KeyStore keyStore) throws Exception { 
     final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); 
     trustManagerFactory.init(keyStore); 

     final SSLContext context = SSLContext.getInstance("SSL"); 
     context.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); 
    } 

    public static SocketFactory getDefault() { 
     return context.getSocketFactory(); 
    } 

    public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException { 
     return trustedFactory.createSocket(arg0, arg1); 
    } 

    public Socket createSocket(InetAddress arg0, int arg1) throws IOException { 
     return trustedFactory.createSocket(arg0, arg1); 
    } 

    public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) throws IOException, UnknownHostException { 
     return trustedFactory.createSocket(arg0, arg1, arg2, arg3); 
    } 

    public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException { 
     return trustedFactory.createSocket(arg0, arg1, arg2, arg3); 
    } 
} 

Espero que compila, ¡no puedo probarlo por el momento! También tenga en cuenta la pereza con la cláusula throws en initTrustedSSLSocketFactory.

A continuación, cuando se configura el entorno LDAP, utilice

TrustedSSLSocketFactory.initTrustedSSLSocketFactory(keyStore); 
env.put("java.naming.ldap.factory.socket", TrustedSSLSocketFactory.class.getName()) 

de una manera similar al código de ejemplo en platinumsolutions. Espero que esto sea más de lo que estás buscando?

+0

Gracias laz por la solución. La última vez no hice la pregunta correctamente. Se supone que debo conectarme al servidor de Active Directory no HTTPS y necesito validar la información del usuario. En este caso, no proporcionaron ninguna URL, me dieron un certificado, un archivo .jks. Así que HTTPURLConnection no será una buena idea, creo. Supongo que después de obtener el SSLContext necesito llamar a una API diferente para verificarlo. Por favor, avíseme si tiene alguna otra idea. -Padur – SPD

+0

Hola Laz..sorry responder tarde. Entiendo lo que está tratando de decir. No pude entender qué hace el programa (código de muestra en platinumsolutions). ¿Pasando la seguridad? – SPD

+0

Sí, ese código platinumsolutions está pasando por alto la validación del certificado (ver http://blog.platinumsolutions.com/node/79). Tómelo como un ejemplo del concepto más que exactamente lo que debe hacer. – laz

Cuestiones relacionadas