Accessing secure restful web services using jersey client

client, jdbcrealm, jersey, security

Solution

i have found a good resource regarding my problem. Here it is

http://wiki.open-esb.java.net/attach/RestBCEchoSSL/SslClient.java

I made few changes in my code regarding the given source and it worked perfectly. Actually I was not passing the certificate and key stores properly.

Here is the full code.

package clients;

import com.sun.jersey.api.client.*;
import javax.net.ssl.*;
import java.io.*;
import java.net.Socket;
import java.security.*;
import javax.ws.rs.core.UriBuilder;
import com.sun.jersey.client.urlconnection.HTTPSProperties;

public class JerseyClient {

    private WebResource webResource;
    private Client client;
    //private static final String BASE_URI = "https://localhost:9028/testsecurity2/resources";
    private static final String truststore_path = "D:/Practice Apps/glassfish-3.0.1 Stand Alone/glassfish/domains/domain2/config/cacerts.jks";
    private static final String truststore_password = "changeit";
    private static final String keystore_path = "D:/Practice Apps/glassfish-3.0.1 Stand Alone/glassfish/domains/domain2/config/keystore.jks";
    private static final String keystore_password = "changeit";
    private static final String url = "https://localhost:9029/testsecurity2/resources/manufacturers/";

    public JerseyClient() {
        com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig(); // SSL configuration
        // SSL configuration
        config.getProperties().put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new com.sun.jersey.client.urlconnection.HTTPSProperties(getHostnameVerifier(), getSSLContext()));
        client = Client.create(config);
        webResource = client.resource(url);
    }

    public <T> T get_XML(Class<T> responseType) throws UniformInterfaceException {
        return webResource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
    }

    public <T> T get_JSON(Class<T> responseType) throws UniformInterfaceException {
        return webResource.accept(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
    }

    public void close() {
        client.destroy();
    }

    public void setUsernamePassword(String username, String password) {
        client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(username, password));
    }

    private HostnameVerifier getHostnameVerifier() {
        return new HostnameVerifier() {

            @Override
            public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
                return true;
            }
        };
    }

    private SSLContext getSSLContext() {

        TrustManager mytm[] = null;
        KeyManager mykm[] = null;

        try {
            mytm = new TrustManager[]{new MyX509TrustManager(truststore_path, truststore_password.toCharArray())};
            mykm = new KeyManager[]{new MyX509KeyManager(keystore_path, keystore_password.toCharArray())};
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        SSLContext ctx = null;
        try {
            ctx = SSLContext.getInstance("SSL");
            ctx.init(mykm, mytm, null);
        } catch (java.security.GeneralSecurityException ex) {
        }
        return ctx;
    }

    /**
     * Taken from http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html
     *
     */
    static class MyX509TrustManager implements X509TrustManager {

        /*
         * The default PKIX X509TrustManager9.  We'll delegate
         * decisions to it, and fall back to the logic in this class if the
         * default X509TrustManager doesn't trust it.
         */
        X509TrustManager pkixTrustManager;

        MyX509TrustManager(String trustStore, char[] password) throws Exception {
            this(new File(trustStore), password);
        }

        MyX509TrustManager(File trustStore, char[] password) throws Exception {
            // create a "default" JSSE X509TrustManager.

            KeyStore ks = KeyStore.getInstance("JKS");

            ks.load(new FileInputStream(trustStore), password);

            TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
            tmf.init(ks);

            TrustManager tms[] = tmf.getTrustManagers();

            /*
             * Iterate over the returned trustmanagers, look
             * for an instance of X509TrustManager.  If found,
             * use that as our "default" trust manager.
             */
            for (int i = 0; i < tms.length; i++) {
                if (tms[i] instanceof X509TrustManager) {
                    pkixTrustManager = (X509TrustManager) tms[i];
                    return;
                }
            }

            /*
             * Find some other way to initialize, or else we have to fail the
             * constructor.
             */
            throw new Exception("Couldn't initialize");
        }

        /*
         * Delegate to the default trust manager.
         */
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                pkixTrustManager.checkClientTrusted(chain, authType);
            } catch (CertificateException excep) {
                // do any special handling here, or rethrow exception.
            }
        }

        /*
         * Delegate to the default trust manager.
         */
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                pkixTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException excep) {
                /*
                 * Possibly pop up a dialog box asking whether to trust the
                 * cert chain.
                 */
            }
        }

        /*
         * Merely pass this through.
         */
        public X509Certificate[] getAcceptedIssuers() {
            return pkixTrustManager.getAcceptedIssuers();
        }
    }

    /**
     * Inspired from http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html
     *
     */
    static class MyX509KeyManager implements X509KeyManager {

        /*
         * The default PKIX X509KeyManager.  We'll delegate
         * decisions to it, and fall back to the logic in this class if the
         * default X509KeyManager doesn't trust it.
         */
        X509KeyManager pkixKeyManager;

        MyX509KeyManager(String keyStore, char[] password) throws Exception {
            this(new File(keyStore), password);
        }

        MyX509KeyManager(File keyStore, char[] password) throws Exception {
            // create a "default" JSSE X509KeyManager.

            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(keyStore), password);

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509", "SunJSSE");
            kmf.init(ks, password);

            KeyManager kms[] = kmf.getKeyManagers();

            /*
             * Iterate over the returned keymanagers, look
             * for an instance of X509KeyManager.  If found,
             * use that as our "default" key manager.
             */
            for (int i = 0; i < kms.length; i++) {
                if (kms[i] instanceof X509KeyManager) {
                    pkixKeyManager = (X509KeyManager) kms[i];
                    return;
                }
            }

            /*
             * Find some other way to initialize, or else we have to fail the
             * constructor.
             */
            throw new Exception("Couldn't initialize");
        }

        public PrivateKey getPrivateKey(String arg0) {
            return pkixKeyManager.getPrivateKey(arg0);
        }

        public X509Certificate[] getCertificateChain(String arg0) {
            return pkixKeyManager.getCertificateChain(arg0);
        }

        public String[] getClientAliases(String arg0, Principal[] arg1) {
            return pkixKeyManager.getClientAliases(arg0, arg1);
        }

        public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
            return pkixKeyManager.chooseClientAlias(arg0, arg1, arg2);
        }

        public String[] getServerAliases(String arg0, Principal[] arg1) {
            return pkixKeyManager.getServerAliases(arg0, arg1);
        }

        public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
            return pkixKeyManager.chooseServerAlias(arg0, arg1, arg2);
        }
    }
}

and code to run the client in main class

public static void main(String[] args) {

    JerseyClient client = new JerseyClient();
    client.setUsernamePassword("testClient", "secret");
     Object response = client.get_XML(String.class);
     System.out.println(response);
     // do whatever with response
     client.close();
}

Problem

I have created web services based on Jersey (auto generated via Netbeans). I have also created a user names “testClient” with password “secret” and created User group “Users” and used file Realm using glassfish 3.0.1 admin console. I have also mapped web.xml and sun-web.xml accordingly. My web services are secured successfully; as I access the web site I receive a security warning and then I am prompt to give username and password to access any content of the website. It is working fine when accessed via web browser. Now I have written a simple client based on jersey and tried to access the web services offered by the 1st project; the client code is here Auto generated Jersey client code ``` public class JerseyClient { private WebResource webResource; private Client client; private static final String BASE_URI = "https://localhost:9028/testsecurity2/resources"; public JerseyClient() { com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig(); // SSL configuration // SSL configuration config.getProperties().put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new com.sun.jersey.client.urlconnection.HTTPSProperties(getHostnameVerifier(), getSSLContext())); client = Client.create(config); webResource = client.resource(BASE_URI).path("manufacturers"); } public <T> T get_XML(Class<T> responseType) throws UniformInterfaceException { return webResource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType); } public <T> T get_JSON(Class<T> responseType) throws UniformInterfaceException { return webResource.accept(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType); } public void close() { client.destroy(); } public void setUsernamePassword(String username, String password) { client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(username, password)); } private HostnameVerifier getHostnameVerifier() { return new HostnameVerifier() { @Override public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { return true; } }; } private SSLContext getSSLContext() { javax.net.ssl.TrustManager x509 = new javax.net.ssl.X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException { return; } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException { return; } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } }; SSLContext ctx = null; try { ctx = SSLContext.getInstance("SSL"); ctx.init(null, new javax.net.ssl.TrustManager[]{x509}, null); } catch (java.security.GeneralSecurityException ex) { } return ctx; } } ``` Code in Main Method; uses auto generated code ``` JerseyClient client = new JerseyClient(); client.setUsernamePassword("testClient", "secret"); Object response = client.get_XML(String.class); // do whatever with response client.close(); ``` Results: ``` Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:128) at com.sun.jersey.api.client.filter.HTTPBasicAuthFilter.handle(HTTPBasicAuthFilter.java:78) at com.sun.jersey.api.client.Client.handle(Client.java:457) at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557) at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:69) at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:451) at clients.JerseyClient.get_XML(JerseyClient.java:23) at clients.NewMain1.main(NewMain1.java:20) Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:808) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1049) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318) at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:215) at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:126) ... 7 more Caused by: java.io.EOFException: SSL peer shut down incorrectly at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:333) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:789) ... 17 more Java Result: 1 ``` I also want to inform that these are two different projects running on different servers both are glassfish 3.0.1. I also tried to run client and services on the same server but all in vain. I am stuck; kindly help me. Cheers!

Original source