15

If I have:

System.setProperty("javax.net.ssl.keyStore", '/etc/certificates/fdms/WS1001237590._.1.ks');
System.setProperty("javax.net.ssl.keyStorePassword", 'DV8u4xRVDq');
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");

I'm able to open a secure connection without a problem.

However, I'd like to have the certificates stored directly in the war, so I use: (The file input stream will eventually become a resource stream, but I'm doing this to get it to work.)

System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("/etc/certificates/fdms/WS1001237590._.1.ks"), "DV8u4xRVDq".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "DV8u4xRVDq".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);

Now, if I open the same connection, I get: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

3
  • What are you then doing with the SSLContext instance sc? Are you using it to create a SocketFactory and setting that into the object making the connections? Commented Jul 14, 2010 at 18:55
  • I'm not doing anything with it. I'm using Axis to then connect to the web service. Commented Jul 14, 2010 at 19:56
  • 1
    Remember to close the FileInputStream, sample code in the javadoc of KeyStore explicitly closes it. Commented Dec 23, 2013 at 12:40

5 Answers 5

11

I had to do something similar a while back. I had a certificate file and I had to figure out a way to load it in and use it for an SSL connection. Hopefully what I did will help you out.

First I had to create a trust manager:

public class MyX509TrustManager implements X509TrustManager {

    X509TrustManager pkixTrustManager;

    MyX509TrustManager() throws Exception {

        String certFile = "/certificates/MyCertFile.cer";

        Certificate myCert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));

        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(null, "".toCharArray());
        keyStore.setCertificateEntry("myCert", myCert);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
        trustManagerFactory.init(keyStore);

        TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();

        for(TrustManager trustManager : trustManagers) {
            if(trustManager instanceof X509TrustManager) {
                pkixTrustManager = (X509TrustManager) trustManager;
                return;
            }
        }

        throw new Exception("Couldn't initialize");
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        pkixTrustManager.checkServerTrusted(chain, authType);
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        pkixTrustManager.checkServerTrusted(chain, authType);
    }

    public X509Certificate[] getAcceptedIssuers() {
        return pkixTrustManager.getAcceptedIssuers();
    }
}

After that I had to create a socket factory that used my trust manager:

public class MySSLProtocolSocketFactory implements SecureProtocolSocketFactory {

    private SSLContext sslContext = null;

    public MySSLProtocolSocketFactory() {
        super();
    }

    private static SSLContext createMySSLContext() {
        try {
            MyX509TrustManager myX509TrustManager = new MyX509TrustManager();
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new MyX509TrustManager[] { myX509TrustManager}, null);
            return context;
        }

        catch(Exception e) {
            Log.error(Log.Context.Net, e);
            return null;
        }
    }

    private SSLContext getSSLContext() {
        if(this.sslContext == null) {
            this.sslContext = createMySSLContext();
        }

        return this.sslContext;
    }

    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
    }

    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
        if(params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }

        int timeout = params.getConnectionTimeout();
        SocketFactory socketFactory = getSSLContext().getSocketFactory();

        if(timeout == 0) {
            return socketFactory.createSocket(host, port, localAddress, localPort);
        }

        else {
            Socket socket = socketFactory.createSocket();
            SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
            SocketAddress remoteAddr = new InetSocketAddress(host, port);
            socket.bind(localAddr);
            socket.connect(remoteAddr, timeout);
            return socket;
        }
    }

    public Socket createSocket(String host, int port) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(host, port);
    }

    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    public boolean equals(Object obj) {
        return ((obj != null) && obj.getClass().equals(MySSLProtocolSocketFactory.class));
    }

    public int hashCode() {
        return MySSLProtocolSocketFactory.class.hashCode();
    }
}

Then I used that socket factory to send my POST:

Protocol.registerProtocol("myhttps", new Protocol("myhttps", new MySSLProtocolSocketFactory(), 443));

PostMethod postMethod = new PostMethod("myhttps://some.url.here");

HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);

The only thing I couldn't figure out was how to simply add the certificate file to the regular keystore. All the example source code I found during my research pointed to creating a socket factor and then registering a protocol with that socket factory. Perhaps there is a way to simply use the socket factory to make a connection without registering a protocol; I haven't investigated that thoroughly. In my particular situation, creating a specific protocol was necessary. Hopefully this will get your further along the way. I admit it seems a bit roundabout; I felt the same way when I did it initially. But this was the only way I got it to work. Maybe other people have a better solution.

Sign up to request clarification or add additional context in comments.

Comments

4

For posterity's sake, all of this was far too complicated, and we pretty much just had a check in the static block:

if( environment == 'production') {
    System.setProperty("javax.net.ssl.keyStore",                    '/etc/certificates/prod/keystore.ks');
    System.setProperty("javax.net.ssl.keyStorePassword",            'password');
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
} else {
    System.setProperty("javax.net.ssl.keyStore",                    '/etc/certificates/test/keystore.ks');
    System.setProperty("javax.net.ssl.keyStorePassword",            'password');
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
}

2 Comments

I'm facing a similar issues and after setting the system properties I get : Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:323) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217) Any ideas ?
"if( environment == 'production') {... System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true"); " - I really hope that is a typo :-/.
3

With Axis, I think you need to configure its SSLSocketFactory via:

AxisProperties.setProperty("axis.socketSecureFactory",
    "com.example.MySSLSocketFactory");

where com.example.MySSLSocketFactory is your class that implements org.apache.axis.components.net.SecureSocketFactory (you could extend org.apache.axis.components.net.JSSESocketFactory perhaps).

In the create method, create a socket using the socket factory obtained from the SSLContext you've configured.

1 Comment

The one line of code you gave solves the problem and then it's up to the developer to implement a custom SecureSocketFactory.
2

If you want, here's an API to create SSLSocket and SSLServerSocket easily:

https://github.com/gpotter2/SSLKeystoreFactories

It does not require any other jars.... just get the files and use them like:

SSLSocket s = SSLSocketKeystoreFactory.getSocketWithCert(ip, port, 
   Main.class.getResourceAsStream("/mykey.jks"), "password")

Or:

SSLServerSocket s = SSLServerSocketKeystoreFactory.getSocketWithCert(port, 
   Main.class.getResourceAsStream("/mykey.jks"), "password")

That's much easier to use :)

Comments

0

I had similar problem, I solved creating a function that returns an SSL context using a keystore coming from and input stream.

   protected SSLContext getSslCtx(InputStream is, String password) {
    try {
        // Load keystore
        KeyStore keystore = KeyStore.getInstance("JKS");
        keystore.load(is, password.toCharArray());

        // Load trust manager
        TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustMgrFactory.init(keystore);

        // Load key manager
        KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyMgrFactory.init(keystore, password.toCharArray());

        // Create SSL context
        SSLContext ctx = SSLContext.getInstance("TLSv1.2");
        ctx.init(keyMgrFactory.getKeyManagers(), trustMgrFactory.getTrustManagers(), null);
        return ctx;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.