mikeobrien.net Curriculum Vitae Blog Labs
Saturday, April 25, 2009

I have run into a couple of instances where I've had to manually add a WSS UsernameToken to consume a WCF service with the basicHttpBinding and a UsernameToken. The client could not understand the WSS timestamp returned by WCF (Notably ColdFusion/Axis1.2 w/ no wss4j). If you are trying to achieve improbability and want to just turn this off there isn't a way to disable the timestamp in the basicHttpBinding or wsHttpBinding through configuration but you can use the customBinding. To emulate basicHttpBinding with transport security and message credentials but with the timestamp off you can define the custom binding as follows:

...
<bindings>
  <customBinding>
    <binding name="MyBinding">
      <security authenticationMode="UserNameOverTransport" includeTimestamp="false" />
      <textMessageEncoding messageVersion="Soap11" />
      <httpsTransport />
    </binding>
  </customBinding>
</bindings>
...
Saturday, April 25, 2009 3:28:30 AM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Friday, April 24, 2009

The following are a couple of things I've discovered about web service namespaces while working on interoperability:

1) If your not going to use a URL for your namespace you need to prepend your namespace with a uri scheme (IE: "myscheme:MyNamespace") to signify that it is absolute. The urn scheme is an appropriate choice but this isn't required as the namespace must simply be a URI. The Microsoft stacks don't seem to mind if the scheme is not present but the Apache Xml Security library is finicky about this (Thinking its a relative namespace if its not there), you will see the following error on the client side if you don't:

org.apache.xml.security.c14n.CanonicalizationException: Element ns1:doSomething has a relative namespace: xmlns:ns1="MyNamespace"

2) Use the same namespace for your binding namespace, behavior namespace, and all service and data contract namespaces. WCF will break out WSDL artifacts from different namespaces into different files and some proxy generators don't like this (Notably ColdFusion).

Friday, April 24, 2009 8:09:06 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Wednesday, April 22, 2009

So here is the scenario on the service side: WCF, basicHttpBinding, UsernameToken Profile (No signing or encryption). On the client side we are working in Eclipse Ganymede, JRE 6, JDK 1.6 Update 13. The following steps outline how to consume the WCF service.

1) Download wss4j-1.5.7.jar from here (Pick a mirror) and save it to your Eclipse plugins folder (Or where ever you like). More information about wss4j can be found here.

2) Download xml-security-bin-1_4_2.zip from here and unzip it into temporary folder. Copy the contents of the libs folder into a subfolder under the Eclipse plugins folder called org.apache.xml.security_1.4 (Or where ever you like). More information about Apache XML Security can be found here.

3) If you haven't already done so create a new Java project and add references to the wss4j-1.5.7.jar and org.apache.xml.security_1.4\xmlsec-1.4.2.jar file.

4) If you haven't already, install the Web Services Tools (WST). Go to "Help|Software Updates" and select the "Available Software" tab. Expand the "Web Tools (WTP) Update Site" node and check the "Web Tools Platform (WTP) x.x.x" node. Then click the "Install" button. Opt to restart the IDE after the install.

image

5) Next right click your project and select "New|Other". Scroll down and expand the "Web Services" node and select "Web Service Client" and click Next:

image

6) Enter the url to the WSDL and make sure the "Client type" is set to "Java Proxy". Next under "Configuration" the "Web service runtime" should be "Apache Axis" and the "Client project" should be your project, if not click the property link and make the appropriate modifications. Move the slider all the way down until it is set to "Develop client".

image

7) Create a new class called Credentials and paste in the following code:

import javax.security.auth.callback.Callback;
import org.apache.axis.client.Stub;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.handler.WSHandlerConstants;
import javax.security.auth.callback.CallbackHandler;

public class Credentials
{
    static class PasswordCallback implements CallbackHandler {
        private String password;
        public PasswordCallback(String password)
        { this.password = password; }
        public void handle(Callback[] callbacks) 
        {((WSPasswordCallback)callbacks[0]).setPassword(this.password); }
    }
    
    public static void Add(Stub stub, String username, String password)
    {
        stub._setProperty(WSHandlerConstants.USER, username);
        stub._setProperty(WSHandlerConstants.PW_CALLBACK_REF, 
                            new PasswordCallback(password));
    }
}

8) Create a new file called ServiceConfig.wsdd and paste in the following xml:

<deployment xmlns="http://xml.apache.org/axis/wsdd/" 
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
  <transport name="http" 
             pivot="java:org.apache.axis.transport.http.HTTPSender"/>
  <globalConfiguration >
    <requestFlow >
      <handler type="java:org.apache.ws.axis.security.WSDoAllSender" >
        <parameter name="action" value="UsernameToken"/>
        <parameter name="passwordType" value="PasswordText"/>
      </handler>
    </requestFlow>
    <responseFlow>
      <handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
        <parameter name="action" value="Timestamp"/>
      </handler>
    </responseFlow>
  </globalConfiguration >
</deployment>

9) If one is not already created then create a class called Startup and paste in the following code. Note, you must specify the bracketed portions as they will be named differently depending on the service and its WSDL.

import <ServiceNamespace>.*;
import org.apache.axis.*;
import java.rmi.RemoteException;
import org.apache.axis.client.Stub;
import javax.xml.rpc.ServiceException;
import org.apache.ws.security.handler.*;
import org.apache.axis.configuration.FileProvider;

public class Startup 
{
    public static void main(String[] args) throws RemoteException, ServiceException 
    {
        EngineConfiguration config = new FileProvider("ServiceConfig.wsdd");
        <ServiceName>ServiceLocator locator = new <ServiceName>ServiceLocator(config);
        <ServiceInterface> service = locator.get<ServiceStub>();
        
        Credentials.Add((Stub)service, "Username", "P@$$w0rd");

        <ResultType> result = service.<Method>();
        System.out.println(result);
    }
}

 

At this point you should be able to successfully consume the target service.

Wednesday, April 22, 2009 11:22:28 PM (GMT Daylight Time, UTC+01:00)  #   |  Comments [0]  |  Trackback
Creative Commons License