Connecting webservice using server’s certificate with Axis 1.4.

This informations were gathered while dealing with following problem -
access remote webservice from java application, using server’s certificate/ private key. Target service was Yandex, as it’s available only for registered user I’ll skip credentials and user details. Client’s machine was some distribution of Fedora, so I’ll explain this case, but solutions should work on most of Linux machines.

prerequirements

1. wsdl file of the target webservice ( for yandex it’s http://soap-new.direct.yandex.ru/api.wsdl )
2. login and password for the service.
If you use Yandex you need to create your private key and “certificate request” and than send them using special form .As the result you’ll have
private.key – our private key
cacert.pem, cert.crt – certificates of the server
3. java, of course, security provider ( I used Bouncy Castle, instalation notes are described on their page ), Axis 1.4.

PHP approach

I’ll mention approach in PHP, as I found code snippet for it. Here, the case is really really simple ( example requires NuSoap, nice SOAP tool for PHP – download it and put into “lib/” to make first import correct ).


<?php
require_once('lib/nusoap.php');
$proxyhost = '';
$proxyport = '';
$proxyusername = '';
$proxypassword = '';
$wsdlurl = "http://soap-new.direct.yandex.ru/api.wsdl";
$client = new nusoap_client($wsdlurl, 'wsdl', $proxyhost, $proxyport,
$proxyusername, $proxypassword);
$client->authtype = 'certificate';
$client->decode_utf8 = 0;
$client->soap_defencoding = 'UTF-8';
$client->certRequest['sslcertfile'] = 'cert.crt';
$client->certRequest['sslkeyfile'] = 'private.key';
$client->certRequest['cainfofile'] = 'cacert.pem';  $client->username=__WS_USERNAME__; $client->password=__WS_PASSWORD__;
$params[] = __WS_USERNAME__;
$result = $client->call('PingAPI', array());  // PingAPI is the method of the service which returns '1' if correctly called

print_r($result);  print_r($client->faultdetail); print_r($client->error_str); print_r($client->faultstring); print_r($client->responseData); print_r($client->requestHeaders);
if($client->fault)
{
echo "FAULT : " . $client->fault;
}
?>

Java keystore.

Truststore is the place where we will hold all informations about service providers we trust. In our case we need to create java’s keystore (.jks file). Please create java kaystore named keystore.jks and import to it mentioned certificate, using your own password ( __JKS_PASSWORD__ ) which, should be different than server’s one.

Stub and working code.

Let’s create stub from WSDL file. Than, we import generated files ( or better, pack them into jar befor – you’ll find it more handy in the future). We must create configuration file ( app-client-config.wsdd ) which we can put in the src/ directory.

Content of app-clinet-config.wsdd would be :


<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<globalConfiguration>
<parameter name="disablePrettyXML" value="true" />
<requestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="action" value="Signature" />
<parameter name="user" value="__WS_USERNAME__" />
<parameter name="passwordCallbackClass" value="pl.mgorski.client.PWCallback" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="signatureKeyIdentifier" value="DirectReference" />
</handler>
</requestFlow>

<responseFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="action" value="Signature Encrypt" />
<parameter name="encryptionUser" value="__WS_USER__" />
<parameter name="passwordCallbackClass" value="pl.mgorski.client.PWCallback" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="encryptionPropFile" value="crypto.properties" />
<parameter name="encryptionKeyIdentifier" value="X509KeyIdentifier" />
</handler>
</responseFlow>
</globalConfiguration>

<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender" />
<transport name="local" pivot="java:org.apache.axis.transport.local.LocalSender" />
<transport name="java" pivot="java:org.apache.axis.transport.java.JavaSender" />
</deployment>

PWCallback

In this file we apply password for out/in-going requests.


package pl.mgorski.client;
import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;

public class PWCallBack implements CallbackHandler {

/**
* Method handle
*
* @param callbacks
* @throws IOException
* @throws UnsupportedCallbackException
*/
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {

for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];

/*
* here call a function/method to lookup the password for
* the given identifier (e.g. a user name or keystore alias)
* e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier))
* for testing we supply a fixed name here.
*/
pc.setPassword( __WS_PASSWORD__);
} else {
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
}

crypto.properties

[sourcecode language='properties']

In this file, let’s configure keystores. It’s important to protect keystore from unwanted access.

org.apache.ws.security.crypto.merlin.file=/home/__PATH_TO_KEYSTORE__/keystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=__JKS_PASSWORD__
org.apache.ws.security.crypto.merlin.keystore.alias=__JKS_ALIAS__
org.apache.ws.security.crypto.merlin.alias.password=__JKS_PASSWORD__
[/sourcecode]

Working stub.


YandexAPIServiceLocator l = new YandexAPIServiceLocator(new FileProvider("app-client-config.wsdd"));
APIPort port = l.getAPIPort();
((GeocodingSoapBindingStub) port)._setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE);
((GeocodingSoapBindingStub) port)._setProperty(WSHandlerConstants.USER, __WS_USERNAME__);
((GeocodingSoapBindingStub) port)._setProperty(WSHandlerConstants.MUST_UNDERSTAND, "false");
((GeocodingSoapBindingStub) port).setUsername(__WS_USERNAME__);
((GeocodingSoapBindingStub) port).setPassword(__WS_PASSWORD__);
int ping = port.pingAPI();
assertEquals( 1, ping ); // Junit, feel free to use your own check here.

If everything is well configured, this should work. I did it some time ago, so it’s possible that I forgot to past something. If it doesn’t work, let me know!

Enjoy!

[sourcecode language='java']

Tags: , , , , , , , , , , ,

3 Responses to “Connecting webservice using server’s certificate with Axis 1.4.”

  1. ski Says:
    October 10th, 2009 at 16:46

    test

  2. zajefajnyx Says:
    November 23rd, 2009 at 20:26

    Chcemy wiecej :D

  3. Bankowy Says:
    May 4th, 2010 at 23:14

    Blog dodaliśmy do ulubionych :) pozdrawiamy autora.

Leave a Reply