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