Signing WS-Addressing headers in Apache CXF

As WS-Addressing (WS-A) headers can drastically affect the behavior of a Web service, securing these headers is just as important as securing the message payload.  In many cases, these headers and other parts of the message will be integrity constrained and bound to each other using a digital signature as described in the WS-Security (WS-S) specification.  The tricky part about signing WS-A headers is that they may or may not always be present in a message and WSS4J uses a static configuration value for which elements to sign.  This static configuration isn’t an issue in of itself, but when combined with WSS4J’s behavior when an element to be signed cannot be found in the message, the configuration can result in blank messages being returned from the server.  This article discusses how to dynamically detect and add the WS-A headers to the message signature.

WS-Addressing Headers

WS-Addressing conveys WS-Addressing message addressing properties (MAPs) in a SOAP Header.  These properties can affect the processing of the message and where/how the response message is sent.  The MAPs and their corresponding XML infoset are defined in section 3 of the WS-Addressing 1.0 – Core specification.  The use of MAPs in a SOAP envelope are described by the WS-Addressing 1.0 – SOAP Binding specification.  Finally, the requirements for the inclusion of the MAPs in the messages of a WSDL defined service are defined in section 5 of the WS-Addressing 1.0 – WSDL Binding specification.  It is the default values assigned to a MAP in the core specification and the variation in required MAPs for a given WSDL MEP as defined in the WSDL binding specification that combine to create the subtle variances in the presence of the header elements in any given message.  This variance alone does not typically cause any issues unless you are trying to configure WSS4J to sign these header elements.

The Signing Problem

When configuring CXF without the help of WS-SecurityPolicy (WS-SP), the list of message elements to be signed is set as a configuration option on the WSS4J out interceptor.  The following code fragment illustrates a typical WSS4J configuration in CXF using CXF’s Spring integration.

<!-- Configure the outbound WSS4J interceptor. -->
<bean id="wss4jOutInterceptor">
  <constructor-arg>
    <!-- The entry keys in this map are defined in
         org.apache.ws.security.handler.WSHandlerConstants
         but are referenced as String literals here for brevity over
         the use of the util:constant element. -->
    <map>
      <entry key="action" value="Timestamp Signature" />
      <!-- This is the fallback user for sig/enc if the sig/enc specific
           properties are not set.  WSS4JOutInterceptor looks for it
           and fails if it is not set. -->
      <entry key="user" value="test-service-1 (test-ca-1)" />

      <!-- Timestamp TTL in seconds. -->
      <entry key="timeToLive" value="60" />

      <!-- The certificate alias in the signature crypto config to sign
           the message with.  The password is retrieved from the callback handler. -->
      <entry key="signatureUser" value="test-service-1 (test-ca-1)" />
      <!-- Signature key attachment method.  We want to put the token
           directly in the header and not use a reference. -->
      <entry key="signatureKeyIdentifier" value="DirectReference" />
      <!-- Defines the property name that contains a Properties object with the desired
           settings in it.  Better than loading a static file from the classpath when using
           Spring. -->
      <entry key="SignaturePropRefId" value="signatureProperties" />
      <!-- The entry that actually contains the Properties object for
           the signature crypto configuration.  See SignaturePropRefId. -->
      <entry key="signatureProperties" value-ref="signatureProperties" />
      <!-- The parts of the response to sign. -->
      <entry key="signatureParts" value="{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body;{Element}{http://www.w3.org/2005/08/addressing}Action;{Element}{http://www.w3.org/2005/08/addressing}MessageID..." />

      <!-- The reference to the callback handler for retrieving passwords
           for private keys in the signature and encryption crypto configurations. -->
      <entry key="passwordCallbackRef" value-ref="pwCallback" />
    </map>
  </constructor-arg>
</bean>

Line 31 of the preceding XML configures the elements to be signed by WSS4J when it creates the message signature.  If WSS4J cannot find one of the elements listed in the signatureParts configuration entry, it throws an exception.  On the server side, if this exception is throw while handling another fault, say a fault triggered by an inbound security problem, CXF simply gives up and returns an empty response message.  This message is really and truly empty, no SOAP Body, no SOAP Envelope.  Depending on what went wrong to cause the original fault, the current message exchange pattern in use, and the validity of the inbound WS-A headers, CXF can provide some or all of the WS-A headers in a response.  When CXF only provides some of the headers in the response, this static configuration breaks down.

The Solution

There are two solutions to this problem in CXF: 1) configure security using WS-SP and 2) dynamically configure the WS-A headers to be signed.  While option 1 solves the problem, it also introduces the complexity of WS-SP.  For those not wishing to or unable to use WS-SP, solution 2 is described below.

The issue above may be resolved by dynamically determining the WS-A headers in the outbound message and adding them to the WSS4J signatureParts configuration option only when present in the message.  This feat is accomplished through the use of a custom CXF interceptor.  The interceptor works by inspecting the outbound message for all possible WS-A headers and adding the present headers to the existing signatureParts configuration property value.

The following code fragments illustrate how to configure this custom interceptor on the server side.  The client side configuration is nearly identical and can be seen in the complete working example in Subversion.

<jaxws:endpoint
  serviceName="service:CustomerServiceService"
  endpointName="service:CustomerServicePort"
  address="http://localhost:8080/services/CustomerService"
  wsdlLocation="classpath:/wsdl/CustomerService.wsdl"
  implementor="#defaultCustomerService">
  <jaxws:features>
    <!-- Configure the WS-Addressing feature. -->
    <addressing:addressing addressingRequired="true"
      allowDuplicates="false"/>
  </jaxws:features>
  <jaxws:inInterceptors>
    <ref bean="wss4jInInterceptor"/>
  </jaxws:inInterceptors>
  <jaxws:outInterceptors>
    <ref bean="wss4jOutInterceptor"/>
    <ref bean="dynamicWsaSignaturePartsInterceptor"/>
  </jaxws:outInterceptors>
  <jaxws:outFaultInterceptors>
    <ref bean="wss4jOutInterceptor"/>
    <ref bean="dynamicWsaSignaturePartsInterceptor"/>
  </jaxws:outFaultInterceptors>
  <jaxws:properties>
    <!-- The parts of the response to sign.  Include: Body, token,
         timestamp at a minimum. -->
    <entry key="signatureParts" value="{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body; Token; {Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp" />
  </jaxws:properties>
</jaxws:endpoint>

<bean id="dynamicWsaSignaturePartsInterceptor"
      class="valeri.blog.examples.cxf_wsa_sig.DynamicWsaSignaturePartsInterceptor"/>

<!-- Configure the outbound WSS4J interceptor. -->
<bean id="wss4jOutInterceptor">
  <constructor-arg>
    <!-- The entry keys in this map are defined in
         org.apache.ws.security.handler.WSHandlerConstants
         but are referenced as String literals here for brevity over
         the use of the util:constant element. -->
    <map>
      <entry key="action" value="Timestamp Signature" />
      <!-- This is the fallback user for sig/enc if the sig/enc specific
           properties are not set.  WSS4JOutInterceptor looks for it
           and fails if it is not set. -->
      <entry key="user" value="test-service-1 (test-ca-1)" />

      <!-- Timestamp TTL in seconds. -->
      <entry key="timeToLive" value="60" /><br />
      <!-- The certificate alias in the signature crypto config to sign
           the message with.  The password is retrieved from the callback handler. -->
      <entry key="signatureUser" value="test-service-1 (test-ca-1)" />
      <!-- Signature key attachment method.  We want to put the token
           directly in the header and not use a reference. -->
      <entry key="signatureKeyIdentifier" value="DirectReference" />
      <!-- Defines the property name that contains a Properties object with the desired
           settings in it.  Better than loading a static file from the classpath when using
           Spring. -->
      <entry key="SignaturePropRefId" value="signatureProperties" />
      <!-- The entry that actually contains the Properties object for
           the signature crypto configuration.  See SignaturePropRefId. -->
      <entry key="signatureProperties" value-ref="signatureProperties" /><br />
      <!-- The reference to the callback handler for retrieving passwords
           for private keys in the signature and encryption crypto configurations. -->
      <entry key="passwordCallbackRef" value-ref="pwCallback" />
    </map>
  </constructor-arg>
</bean>

Lines 17 and 21 add the new custom interceptor to the out and outFault interceptor chains of the server.  This addition ensures that both regular and fault responses from the server are equally secure.  These lines reference the DynamicWsaSignaturePartsInterceptor instance defined on line 30.

The next major difference is in the relocation of the signatureParts configuration option from the properties passed to WSS4JOutInterceptor to the properties defined on the service itself.  This configuration option now appears on line 26.  Note that the relocated property value also contains no configuration relating to any WS-A header elements.  This configuration property is relocated to optimize the performance of WSS4J.  When this property is left in its original position, the DynamicWsaSignaturePartsInterceptor is forced to manipulate the service interceptor chain in a way that prevents the caching of cryptographic configuration information.  By moving the configuration location, this caching can still occur.  The DynamicWsaSignaturePartsInterceptor can handle the configuration in either location but produces a warning in the logs when the configuration is not moved to the service definition.

Trying It Out

You will need a Subversion client, Maven 2.2.1, Java 1.5/1.6, and access to the Internet to retrieve the needed dependencies from the Maven repositories in order to build the sample code and the DynamicWsaSignaturePartsInterceptor interceptor.  The following instructions will guide you through retrieving the source code, building the code, and running the example.

  1. Using a Subversion client, checkout a copy of the code from  https://davidvaleri.googlecode.com/svn/projects/examples/cxf-wsa-sig/tags/cxf-wsa-sig-1.0 to a local working directory.
  2. Execute mvn install in the local working directory.  The build output will display similar to the following
    [INFO] BUILD SUCCESSFUL
    [INFO] -------------------------------------------------------------
    [INFO] Total time: 8 seconds
    [INFO] Finished at: Tue Sep 14 21:38:41 EDT 2010
    [INFO] Final Memory: 34M/254M
    [INFO] -------------------------------------------------------------
  3. Execute mvn -P server in the working directory.  The example server will start and listen for incoming message.
  4. Using another console window, execute mvn -P client in the working directory.  The example client will start and send messages to the server.  The server will log the messages to the console window for review.

If you review the captured messages to and from the server, you can observe the WS-Security Signature over all WS-Addressing headers in the message.  In the example fragment below, each WS-Addressing header, on lines 23-28, contains a wsu:Id element containing an ID that is referenced from the embedded message signature on lines 6, 9, 12, and 15.

<soap:Header>
    <wsse:Security xmlns:wsse="..." soap:mustUnderstand="1">
    ...
    <ds:Signature xmlns:ds="..." Id="Signature-39">
      <ds:SignedInfo>
        <ds:Reference URI="#id-41">
          ...
        </ds:Reference>
        <ds:Reference URI="#id-42">
          ...
        </ds:Reference>
        <ds:Reference URI="#id-43">
          ...
        </ds:Reference>
        <ds:Reference URI="#id-44">
          ...
        </ds:Reference>
      </ds:SignedInfo>
      ...
    </ds:Signature>
    ...
  </wsse:Security>
  <Action xmlns="..." xmlns:wsu="..." wsu:Id="id-41">http://customerservice.example.com/CustomerService/getCustomersByName</Action>
  <MessageID xmlns="..." xmlns:wsu="..." wsu:Id="id-44">urn:uuid:a2d9d794-60bc-48f8-b549-d7c4fb7ca083</MessageID>
  <To xmlns="..." xmlns:wsu="..." wsu:Id="id-42">http://localhost:8080/services/CustomerService</To>
  <ReplyTo xmlns="..." xmlns:wsu="..." wsu:Id="id-43">
    <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
  </ReplyTo>
</soap:Header>
<jaxws:endpoint
serviceName=”service:CustomerServiceService”
endpointName=”service:CustomerServicePort”
address=”http://localhost:8080/services/CustomerService&#8221;
wsdlLocation=”classpath:/wsdl/CustomerService.wsdl”
implementor=”#defaultCustomerService”>
<jaxws:features>
<!– Configure the WS-Addressing feature. –>
<addressing:addressing addressingRequired=”true”
allowDuplicates=”false”/>
</jaxws:features>
<jaxws:inInterceptors>
<ref bean=”wss4jInInterceptor”/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean=”wss4jOutInterceptor”/>
<ref bean=”dynamicWsaSignaturePartsInterceptor”/>
</jaxws:outInterceptors>
<jaxws:outFaultInterceptors>
<ref bean=”wss4jOutInterceptor”/>
<ref bean=”dynamicWsaSignaturePartsInterceptor”/>
</jaxws:outFaultInterceptors>
<jaxws:properties>
<!– The parts of the response to sign.  Include: Body, token,
timestamp at a minimum. –>
<entry key=”signatureParts” value=”{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body; Token; {Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp” />
</jaxws:properties>
</jaxws:endpoint> 

<bean id=”dynamicWsaSignaturePartsInterceptor”
class=”valeri.blog.examples.cxf_wsa_sig.DynamicWsaSignaturePartsInterceptor”/>

<!– Configure the outbound WSS4J interceptor. –>
<bean id=”wss4jOutInterceptor”>
<constructor-arg>
<!– The entry keys in this map are defined in
org.apache.ws.security.handler.WSHandlerConstants
but are referenced as String literals here for brevity over
the use of the util:constant element. –>
<map>
<entry key=”action” value=”Timestamp Signature” />
<!– This is the fallback user for sig/enc if the sig/enc specific
properties are not set.  WSS4JOutInterceptor looks for it
and fails if it is not set. –>
<entry key=”user” value=”test-service-1 (test-ca-1)” />

<!– Timestamp TTL in seconds. –>
<entry key=”timeToLive” value=”60″ /><br />
<!– The certificate alias in the signature crypto config to sign
the message with.  The password is retrieved from the callback handler. –>
<entry key=”signatureUser” value=”test-service-1 (test-ca-1)” />
<!– Signature key attachment method.  We want to put the token
directly in the header and not use a reference. –>
<entry key=”signatureKeyIdentifier” value=”DirectReference” />
<!– Defines the property name that contains a Properties object with the desired
settings in it.  Better than loading a static file from the classpath when using
Spring. –>
<entry key=”SignaturePropRefId” value=”signatureProperties” />
<!– The entry that actually contains the Properties object for
the signature crypto configuration.  See SignaturePropRefId. –>
<entry key=”signatureProperties” value-ref=”signatureProperties” /><br />
<!– The reference to the callback handler for retrieving passwords
for private keys in the signature and encryption crypto configurations. –>
<entry key=”passwordCallbackRef” value-ref=”pwCallback” />
</map>
</constructor-arg>
</bean>

About these ads
This entry was posted in Uncategorized and tagged , , . Bookmark the permalink.

5 Responses to Signing WS-Addressing headers in Apache CXF

  1. Yogesh Chawla says:

    Hi David,
    I really like configuring the interceptor like this:

    Defines the property name that contains a Properties object with the desired settings in it. Better than loading a static file from the classpath when using Spring.

    However, this doesn’t work with CXF 2.4.6 with WSS4J 1.6.x. Have you had any luck with this? I posted some details on the CXF list:

    http://cxf.547215.n5.nabble.com/WS-Security-Properties-Reference-td5505704.html#a5543154

    Any ideas?

  2. Yogesh Chawla says:

    Hi David et all,
    The solution was to make signaturePropRefId lower case and that worked with WSS4J 1.6.x.

    Thanks,
    Yogesh

  3. Ed Bras says:

    Can this still work with CXF 3.0? (WASS4J 2.0) (except from some package/class name changes).
    I tried it, but couldn’t get it to work :(..
    For some reason the soap response, the soap client received, contained still some mime type info such that xml parsing failed. I tried to solve it with the guys in the cxf forum, but it didn’t work and it’s hard to make a testcase for them.
    - Ed

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s