Building a java webservice client
These instructions are intended for University of Washington
clients connecting to C&C webservices that require client
certificate authentication. The particular example
is the University's
ASTRA
webservice.
Hopefully these instructions may also be helpful in other applications.
Import the WSDL
- Use no less than java 1.5.
- Use Apache Axis.
- Use version 1.2. I've not been able to run 2.0 without crashes.
- Process the target service's wsdl with WSDL2Java.
- The test client generated by the '-t' option is helpful to
see how to use the generated soap classes.
- For example, to process the evaluation WSDL from
ASTRA
you might use, with the appropriate classpath,
$ wsdl="https://ucs.admin.washington.edu/astraws/astraws.asmx?wsdl"
$ java WSDL2Java -t $wsdl
- Be sure to include the jars in the AXIS lib directory
when you build the generated classes, in this case the
java files in "edu/washington/...".
Write your classes
The generated test case,
AuthzProviderTestCase.java, shows the
astra package path and a way to make the binding:
package edu.washington.admin.ucs.astra;
...
public void test1AuthzProviderSoapGetAuthz() throws Exception {
edu.washington.admin.ucs.astra.AuthzProviderSoapStub binding;
try {
binding = (edu.washington.admin.ucs.astra.AuthzProviderSoapStub)
new edu.washington.admin.ucs.astra.AuthzProviderLocator().getAuthzProviderSoap();
} catch (javax.xml.rpc.ServiceException jre) {
if(jre.getLinkedCause()!=null)
jre.getLinkedCause().printStackTrace();
throw new junit.framework.AssertionFailedError(
"JAX-RPC ServiceException caught: " + jre);
}
...
Other webservices would be similar. Only the names change.
In practice we can usually get rid of the 'Stub's and just go with,
package edu.washington.admin.ucs.astra;
...
AuthzProviderSoap astra = null;
try {
astra = (AuthzProviderSoapStub) new AuthzProviderLocator().getAuthzProviderSoap();
} catch (javax.xml.rpc.ServiceException jre) {
if(jre.getLinkedCause()!=null) jre.getLinkedCause().printStackTrace();
throw new Exception("JAX-RPC ServiceException caught: " + jre);
}
...
Making a webservice request requires we provide some input parameters.
These are specific to the particular webservice. Astra requires us
to specify an Environment, Privilege, and Party.
Auth auth = new Auth();
Party party = new Party();
Role role = new Role();
Environment env = new Environment();
Privilege priv = new Privilege();
Action action = new Action();
SpanOfControl span = new SpanOfControl();
party.setUwNetid(userid);
env.setCode("eval");
priv.setCode("BulkEmail");
auth.setParty(party);
auth.setEnvironment(env);
auth.setPrivilege(priv);
auth.setRole(role);
auth.setAction(action);
auth.setSpanOfControlCollection(new SpanOfControl[]{span});
Authz az = null;
try {
az = astra.getAuthz(auth);
} catch (Exception e) {
System.err.println("Caught Exception: " + e.getMessage());
}
and if we want to see some of the response:
Auth[] aa = az.getAuthCollection();
try {
for (int i = 0; i<9; i++) {
Auth a = aa[i];
System.out.println("Role: " + a.getRole().getCode());
System.out.println("Action: " + a.getAction().getCode());
}
} catch (ArrayIndexOutOfBoundsException e) {
print something
}
A sample
Astra demo client shows
in more detail how this code can fit together.
Accept the service's CA
If you're accessing a University of Washington service
you need to accept the University's
UW Services CA
certificates.
You could just add them to Java's default certificate
store,
JAVA_HOME/jre/lib/security/cacerts,
but then you're accepting every fly-by-night CA in that
store. Better to make a new store and put in it only
those certificate you have to trust.
- Make a new store and add the UW Service CA to it.
- Get the UWCA
root certificate (PEM format is OK) and save
to a local file, say, "uwca.crt".
- Make a store and add the uwca cert
$ keytool -import -alias uwca -file uwca.crt \
-keystore trusted.jks
- Let your java VM use this store for its CA list. In this case
Java properties should be OK, as all applications ought to be
able to use the same set of CAs. Just run java with the
definition (in this example):
-Davax.net.ssl.trustStore=trusted.jks
Authenticate with a client cert
University C&C servers usually use certificate authentication and
require certificates issued by the University's UWCA. These are
available without charge to authorized administrators of
University systems.
- Request and install a client certificate
The keys and certificates generated by standard keytool
operations will work with openssl servers but will not
work for MS (IIS) servers. The difference is supposedly
related to SSL v1 vs. SSL v3. I suspect, however, it has
something to do with the keytool utility itself. In any case
keytool methods will not work.
This method does work.
- Generate a request with openssl; e.g.,
$ openssl req -nodes -newkey 1024 -keyout mycrt.key -out mycrt.req
- Have that request, mycrt.req in this example, signed
by the
UWCA.
- Retrieve and store your signed certificate in, say, mycrt.crt
- Convert the certificate and key combination to pkcs12 format,
$ openssl pkcs12 -in mycrt.crt -inkey mycrt.key \
-export -out mycrt.pk12 -nodes -CAfile uwca.crt
where the uwca.crt the UWCA's root cert.
- Add the pkcs12 format certificate and key to your certificate store
with the pkcs12import tool. If the store does not exist create it.
The pkcs12import is a part of Sun's JWSDP distribution
(in the xws-security section).
$ keytool -import -file uwca.crt -alias uwca \
-keystore mystore.jks
$ pkcs12import.sh -file mycrt.pk12 -alias mycrt \
-keystore mystore.jks
I use the same password for both the store and the certificate.
Try to remember whatever you entered; "changeit" is easy to remember and everyone knows it.
- Use that certificte with your java applications.
There are many ways to do this. Here are a couple. Setting Java properties
is the easier, but suffers from the global nature of
properties. Every application in the Java VM must use
the same cert and key, and only a single cert may be specified.
For many applications these restrictions are not too severe.
A more flexible method uses a custom socket factory, to
which you can apply different certificates for each connection.
Property method
Several choices:
- Set properties on the java command line:
- -Djavax.net.ssl.keyStore=path_to_your_keystore
- -Djavax.net.ssl.keyStorePassword=your_keystore_password
- Set properties from your code:
- System.setProperty("javax.net.ssl.keyStore","path_to_your_keystore);
- System.setProperty("javax.net.ssl.keyStorePassword","your_keystore_password);
- Set properties in ColdFusion's admin tool
- Run the ColdFusion Administrator application
- Select java and JVM
- Add to the JVM Arguments
- -Djavax.net.ssl.keyStore=path_to_your_keystore
- -Djavax.net.ssl.keyStorePassword=your_keystore_password
- Select Submit Changes
- Restart ColdFusion
SocketFactory method
Axis allows you to override its default, JSSESocketFactory, with
your own. By doing so you can specify a specific certificate for each connection.
An example is this UWSocketFactory, which could be
activated by the Java property
org.apache.axis.components.net.SecureSocketFactory
=edu.washington.smw.UWSocketFactory
The example uses properties, edu.washington.smw.keyStore, for example,
to locate a certificate, but is easily extended to find certificates
based on other parameters (e,g, remote service name). Used in
a Tomcat application it could find certificates based on option settings.