Showing posts with label web service. Show all posts
Showing posts with label web service. Show all posts

Wednesday, 18 May 2016

Web Service Client with Basic Authentication and SSL

Web Service Client with Basic Authentication and SSL

Recently, I had to create a web service client for a web service that uses a number of Web Service Policies. In general, the web service utilizes the following policies:
  • Transport: Service uses one way certificates. Client had to download and check server´s certificate in order to prove the server´s  identity.
  • Authentication: Basic authentication is required to access the URL and the service WSDL.
The following steps were used.
  • Creating the TrustStore: Access the Web Service URL, download the web service certificate and create a x509 trustStore to host the server´s certificate.
  • Create the client Stub: Access the Web Service URL and create the client stub by compiling the WSDL with wsimport.
  • Code and complete the service client. This has the following sub tasks:
    • Code the client to use Basic Authentication
    • Code the client to utilize the trustStore in order to setup SSL session with the server
    • Code the client to call the web method.

Creating the SSL Trustore.

During SSL handshake, the trustStore is used to verify server´s id.
Download the Server´s certificate by hitting the Web Service URL. There you will be prompted for login. You can login with the given user/password.

Then, the certificate is stored in your browser. You can export it easy but that depends to you browser. Chrome for example, the certificate can be downloaded directly as a x509 trustStore like the following image illustrates:



If you want to create the a trustStore manually you need to create a X509 keystore file using Java keytool and then import the server´s public certificate in it. The trustStore will be password protected and the certificate inside the trustStore will be password protected using "password" passphrase:

$ keytool -genkey -alias replserver -keyalg RSA -keystore mykeystore.jks -dname "cn=localhost, ou=IT, o=Continuent, c=DE"  -storepass password -keypass password

Now you have the keyStore. Next you need to import the server´s public certificate in it. In the general case, supposing the Server certificate is the following one plain text file server-certificate.txt then do one of the following actions to:

Check the server´s certificate:
openssl x509 -in server-certificate.txt -text -noout

Delete previous certificate version from the trustStore if any:
keytool -delete -alias myserver-name.com  -keystore mykeystore.jks 

Re-import the server certificate to the trustStore:
keytool -import -alias myserver-name.com -keystore mykeystore.jks  -file server-certificate.txt

Access the Web Service URL and create the client stub by compiling the WSDL with wsimport.

After running your wsimport command directly you should get a message complaining about a missing web authorization file.
What you need to do is create an authorization file (usually the default name/location for it is $HOME_DIRECTORY/.metro/auth, but check the previous error message, you'll get the hint from there).
Inside this file you just write the line: "https://username:password@url?wsdl"

 Now create a file called: wsimport_mysvc.bat and code the following commands:
setlocal
set _JAVA_OPTIONS=%_JAVA_OPTIONS% -Djavax.net.ssl.trustStore=mykeystore.jks -Djavax.net.ssl.keyStorePassword=changeit -Djavax.net.ssl.trustStore=mykeystore.jks
wsimport -s . -verbose -keep -p gr.illumine.wsclient.stub  -extension https://myserver-name.com/wsd/alc_interface?wsdl
endlocal

Doing so, you fulfill both conditions for basic authentication and also for transport/SSL by asking wsimport to examine what is been sent from server against to what is stored in mykeystore.jks

Run the wsimport_mysvc.bat and the client stub files will be created in the package gr.illumine.wsclient.stub

C:\>set _JAVA_OPTIONS= -Djavax.net.ssl.trustStore=cacerts -Djavax.
net.ssl.keyStorePassword=changeit -Djavax.net.ssl.trustStore=cacerts

C:\>wsimport -s . -verbose -keep -p gr.illumine.wsclient.stub  -extension https://myserver-name.com/wsd/alc_interface?wsdl
Picked up _JAVA_OPTIONS:  -Djavax.net.ssl.trustStore=cacerts -Djavax.net.ssl.key
StorePassword=changeit -Djavax.net.ssl.trustStore=cacerts
parsing WSDL...

Code the client

The first thing you have to do is to add a static initializer that will provide the username and password for basic authentication:

public class AlcClient {
 
 private static final Logger log= Logger.getLogger( AlcClient.class.getName() );
 
 /* 
  * Use this static initializer to provide Basic Authentication for the Web Service Consumption
  */
 static {
     java.net.Authenticator.setDefault(new java.net.Authenticator() {

         @Override
         protected java.net.PasswordAuthentication getPasswordAuthentication() {
             return new java.net.PasswordAuthentication("happyuser", "mypassword".toCharArray());
         }
     });
 }

Next, configure your SSL settings in the code, by adding the following system parameters:
        /*
         * Use the following settings to specify how this client will utilize the X509 trust store
         * called mykeystore.jks. In this trustore, it is stored the server´s public certificate
         * Also the trustore/keystores are password protected with a password "password"
         */
        System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl.internal.www.protocol");
        System.setProperty("javax.net.ssl.keyStore","mykeystore.jks");
        System.setProperty("javax.net.ssl.keyStorePassword","password");
        System.setProperty("javax.net.ssl.keyStoreType", "JKS");
        System.setProperty("javax.net.ssl.trustStore","mykeystore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword","password");
        System.setProperty("javax.net.ssl.trustStoreType", "JKS");

Then add some debugging options to debug your SSL session. You are strongly advised to comment out the following code after testing it since it will affect the SSL performance.
        /* Following options enable logging of all communication to the console
         * We are most interested in the request response SOAP Messages   */
        System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
        System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");

Now code the Web Service client instance by using the stub you have created with the wsimport:
 ZALCINTERFACE_Service service = new ZALCINTERFACE_Service( new URL("myserver-name.com/wsd/alc_interface?wsdl"),
     new QName("urn:com:myserver-name:document:sap:soap:functions:mc-style", 
                      "ZALC_INTERFACE"));
  
 /*
  * From this service get the proper port
 */
 ZALCINTERFACE port = service.getZALCINTERFACE(); 

        /* Make the web service call */
        String responseMessage = port.callMyWebMethod();

Get the entire web service client java implementation can be downloaded here