Java Webstart Trust Store SSL
Need some guidance.
I have a Java Webstart application and I want it to connect to a server via SSL. Just add a property like: System.setProperty("javax.net.ssl.trustStore", "my.keystore"); however, it doesn't work since the JAWS program is downloading the file from the server and there is no file on the local filesystem my.keystore. So it was decided to distribute the certificate to all clients.
- Read this truststore as a stream (using the getResourceAsStream method).
- save it in any file on the client computer (sometimes)
- 调用System.setProperty(“ javax.net.ssl.trustStore”,trustStorePath);
However, I'm sure there is a better solution than this.
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 thanks for your patience and trying to learn when you have time. I started writing my own CustomSSLSocketFactory..now I'm bypassing the security...according to the platinum solution's example, if I do...will the information be passed on the wire in clear text?
Now I want to know what to do with the truststore file where I have the "sometruststore.jks" file. what should i do.. do i have my own custom trustmanager software? Please point me in the right direction.
- Padour
You can do this without relying on system properties and the filesystem. It would be cleaner to read the keystore as if you were reading the stream, and create your own SSLSocketFactory.
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 can't see why this wouldn't work with Webstart.
renew:
You mentioned you're connecting to Active Directory, so I'm guessing you'll be using LDAPS as the protocol? If so, maybe the code at this URL could be enlightening? You have to create a subclass (see that platinumsolutions link) that wraps the logic created above and represents the call to create .javax.net.ssl.SSLSocketFactory
BlindSSLSocketFactoryTest
SSLContext
SSLSocketFactory
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);
}
}
Hope it can be compiled, can't test it at the moment! Also note the laziness of the throws
clause with initTrustedSSLSocketFactory
.
Then, when you set up your LDAP environment, use
TrustedSSLSocketFactory.initTrustedSSLSocketFactory(keyStore);
env.put("java.naming.ldap.factory.socket", TrustedSSLSocketFactory.class.getName())
In a manner similar to Platinumsolutions' sample code. Wish this was more of what you were looking for?