views:

612

answers:

1

Hello Experts.

Need some guidance.

I have java webstart app and I want it to connect to a server via SSL.just adding a property like:System.setProperty("javax.net.ssl.trustStore","my.keystore");But since a JAWS program is downloaded from server didn't work and don't have a my.keystore on local file system. So decided to distribute the certificate to all clients.I did the following and it worked.

  1. Read this trust store as a stream (use getResourceAsStream method).
  2. Save it in any file on the client machine (sometemp)
  3. Call System.setProperty ("javax.net.ssl.trustStore", trustStorePath);

But I am sure there must be better solutions than this.. Any ideas to make it better?

        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 appreciate your patience, trying to learn when I get some time. I started writing my own CustomSSLSocketFactory..right now I am bypassing security...based on the example by platinum solutions.If I do that ...will the information pass as a clear text on the network?

Now I wonder what should I do with the truststore file I am having "sometruststore.jks" file. What should I do with that ..Do I have wrie my own custom trustmanager software ? Please guide me in correct direction.

-padur

+1  A: 

You could do it without having to rely on system properties and the file system. Reading the keystore as a stream like you are doing and creating your own SSLSocketFactory would be much cleaner.

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

...

I haven't verified but I see no reason why this shouldn't work through Webstart.

Updated:

You mention that you are looking to looking to connect to active directory so I'm guessing you are going to use LDAPS as the protocol? If so, maybe the code at this URL can serve as inspiration? You'll have to create a subclass of javax.net.ssl.SSLSocketFactory (see BlindSSLSocketFactoryTest at that platinumsolutions link) that wraps the logic above of creating the the SSLContext and delegates calls to the SSLSocketFactory that context.getSocketFactory() creates.

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);
    }
}

Hopefully that compiles, I'm unable to test it at the moment! Also note the laziness with the throws clause on initTrustedSSLSocketFactory.

Then when you setup the LDAP environment, use

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

in a similar manner to the sample code at platinumsolutions. Hopefully this is more of what you're looking for?

laz
Thank you laz for the solution.Last time I didn't ask the question correctly. I am supposed to connect to active directory not HTTPS server and I need to validate user information. In this case they didn't supply any URL, they gave me a certificate a .jks file. So HTTPURLConnection will not a good idea I think. I guess after getting the SSLContext I need to call different API to verify it. Please let me know if you have any other ideas.-Padur
Padur
Hello Laz..sorry responding late.I understand what you are trying to say.I couldn't get what is the program (sample code at platinumsolutions) doing. Is it by passing security?
Padur
Yes, that platinumsolutions code is bypassing certificate validation (see http://blog.platinumsolutions.com/node/79). Take it as an example of the concept more than exactly what you should do.
laz
Hello Laz..I really appreciate your help.When I tried your solution it throws the below :Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at com.sun.jndi.ldap.Connection.createSocket(Connection.java:311) at com.sun.jndi.ldap.Connection.<init>(Connection.java:181)
Padur
Looking into it more, it looks like the static method `getDefault()` was needed on `TrustedSSLSocketFactory`. I modified it to include it above. Give that new code a shot.
laz
Laz..I tried with your new code..was not successfull but I have written my own sample customSSLSocketFactory..I copied in question text area..could you please review and let me know how to proceed.ThanksPadur
Padur
Looks like this is the way I updated the new code above. could you please look at it. Thanks -Padur
Padur
The code formatting in your update seems to be broken.
laz