Java SOAP client with certificate authentication

Maybe you are wondering why a java entry in a databases blog? The answer is simple, at CERN, the Infrastucture and Middleware Services section (aka IMS) is part of the  Database Services , and, among many other things, we take care of the Java application servers for our users.

And now get down to work! In this post I am going to show you how to generate a java client for a SOAP web service. This last one requires client certificate authentication.

For the generation we will use the maven cxf-codegen-plugin. The setup is simple, just declare the plugin in your pom.xml:

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>2.7.4</version>
    <executions>
        <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <inherited>false</inherited>
            <configuration>
                <sourceRoot>${project.build.directory}/generated</sourceRoot>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>${basedir}/src/main/resources/wsdl/HelloWorldWS.wsdl</wsdl>
                        <wsdlLocation>classpath:wsdl/HelloWorldWS.wsdl</wsdlLocation>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <!-- IMPORTANT: invoke it with cxf-codegen:wsdl2java -->
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Couple of hints:

And that's it! Just run the plugin and you are done. If you use the m2e-Maven Integration for Eclipse be careful of prefix your goal with the name of the plugin (cxf-codegen:wsdl2java):

wsdl2java maven goal

In your console output you will have something like this:

Console output

Apache Maven 3.0.4 (r1232337; 2012-01-17 09:44:56+0100)

[DEBUG] Project: ch.cern.it.db.mw:helloworldclient:jar:0.0.1
[DEBUG] Tasks:   [cxf-codegen:wsdl2java]
[DEBUG] Style:   Regular

[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.721s
[INFO] Finished at: Sat Jul 05 18:10:17 CEST 2014
[INFO] Final Memory: 4M/75M

And in your project:

generated java code

Now it is time to code a simple client, but first you need to include the generated code into your project. You can simply move it to your src/main/java folder:

generated code moved to src/main/java

Our client will be something like this:

Sample client
public static void main(String[] args) throws MalformedURLException {
        URL wsdlLocation = new URL(args[1]);
        HelloWorldImplService helloWorldImplService = new HelloWorldImplService(wsdlLocation);
        HelloWorld helloWorld = helloWorldImplService.getHelloWorldImplPort();
        SayHelloWorld parameters = new SayHelloWorld();
        parameters.setArg0("Luis");
        SayHelloWorldResponse helloWorldResponse = helloWorld.sayHelloWorld(parameters);
        System.out.println(helloWorldResponse.getReturn());
    }

And now the authentication. In theory it is very simple, at runtime we only need to add your client keystore with the privateKey to the JVM. Also you will need to add a keystore with the certificates of the trusted CAs:

JAVA OPTIONS
-Djavax.net.ssl.trustStore=/your/path/cacerts.jks
-Djavax.net.ssl.trustStorePassword=cacertsPassword
-Djavax.net.ssl.keyStore=/your/path/client.jks
-Djavax.net.ssl.keyStorePassword=clientPassword

Me, for simplicity, I will added to the VM arguments of my Java run configuration in Eclipse:

runtime java options

And now the tricky part, be sure that your intermediate certificate is associated with the entry where you have your client certificate and its private key! This is, your privateKey in your client.jks should look like this:

Private key certificate chain
Certificate chain length: 2
Certificate[1]:
Owner: CN=Robot: Db Systems...
Issuer: CN=CERN Grid Certification Authority...
.../...
Certificate[2]:
Owner: CN=CERN Grid Certification Authority, DC=cern, DC=ch
Issuer: CN=CERN Root Certification Authority 2...

If you do not do this your client will never send the client certificate during the SSL handshake. Why? Because the client needs to determine if it has a keypair that matches one of the members of the list of trusted CA issued by the server. See this excellent article by Jos Dirksen in DZone: How to Analyze Java SSL Errors.

If you activate the debug traces in the JVM (-Djavax.net.debug=all), at some point you should see something like this (keytool -list -keystore client.jks -v -alias user_client_cern_ca2):

List of server's cert authorities

*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
<CN=CERN Root Certification Authority 2, O=CERN, C=ch>
.../...

Above is part the list of the Cert authorities trusted by the server. And once the ServerHello is finish you should see the how the client certificate is issued:

Client certificate chain issued
matching alias: user_client_cern_ca2
*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=Robot: Db Systems,...
.../...
]
chain [1] = [
[
  Version: V3
  Subject: CN=CERN Grid Certification Authority...
.../...

So in this example "CN=Robot: Db Systems..." is issue by "CN=CERN Grid Certification Authority..." that is issue by "CN=CERN Root Certification Authority 2".

Tha's all folks! Have a happy coding day!

Luis

Links:

 

 

 

 

 

Comments

Add new comment

You are here