Streaming (StAX) WS-Security support in Apache WSS4J™ 2.0.0

Streaming (StAX) WS-Security support in Apache WSS4J™ 2.0.0

Overview of new features

WSS4J 2.0.0 introduces a streaming (StAX-based) WS-Security implementation to complement the existing DOM-based implementation. The DOM-based implementation is quite performant and flexible, but suffers from having to read the entire XML tree into memory. For large SOAP requests this can have a detrimental impact on performance. In addition, for web services stacks such as Apache CXF which are streaming-based, it carries an additional performance penalty of having to explicitly convert the request stream to a DOM Element.

The new StAX-based WS-Security implementation does not read the request into memory, and hence uses far less memory for large requests. It is also more performant in certain circumstances. The StAX-based code offers largely the same functionality as that available as part of the DOM code, and is configured in mostly the same way (via configuration tags that are shared between both stacks). It does not offer the low-level API available in the DOM code to individually construct various WS-Security tokens, but instead must be used by specifying various actions to perform.

As of the time of writing, Apache CXF is the only web services stack to integrate the new WS-Security streaming functionality. To switch to use the streaming code for the manual "Action" based approach, simply change the outbound and inbound interceptors as follows:

  • "org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor" to "org.apache.cxf.ws.security.wss4j.WSS4JStaxOutInterceptor".
  • "org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor" to "org.apache.cxf.ws.security.wss4j.WSS4JStaxInInterceptor".

For the WS-SecurityPolicy based approach of configuring WS-Security, simply set the JAX-WS property SecurityConstants.ENABLE_STREAMING_SECURITY ("ws-security.enable.streaming") to "true".

Limitations of the streaming WS-Security implementation

The new streaming implementation in WSS4J 2.0.0 meets the vast majority of the most common use-cases. However, it does not support everything that the DOM implementation supports. The limitations are:

  • XPath evaluation is not supported apart from certain simple expressions. XPath evaluations are used with WS-SecurityPolicy RequiredElements, SignedElements, (Content)EncryptedElements. XPath expressions that point directly to the element are supported, e.g. /soap:Envelope/soap:Header/wsa:To. See WSS-445.
  • WS-SecurityPolicy "Strict" Layout validation is not enforced. This includes enforcing whether a Timestamp is first or last. See WSS-444.
  • A SymmetricBinding policy with a ProtectTokens assertion is not supported. See WSS-456.
  • The combination of EncryptBeforeSigning + EncryptSignature policies are not supported. See WSS-464.
  • Deriving keys from Username Tokens (Endorsing Username Tokens) are not supported.
  • Endorsing tokens don’t work with Symmetric + Asymmetric binding on the client side, unless the endorsing token is a SAML or IssuedToken.
  • Derived Endorsing Tokens are not supported on the client side.
  • A policy assertion that the PolicyEnforcer does not recognise is not enforced. A top-level assertion in that position is logged at WARN naming the assertion, and setting the system property "org.apache.wss4j.policy.failOnUnsupportedAssertions" to "true" rejects the policy outright instead of under-enforcing it. An assertion nested inside another - inside a binding, a token or an AlgorithmSuite - is dropped without any diagnostic at all, a misspelled name among them. No diagnostic is available for that case: an assertion is offered to every parser in its parent’s inheritance chain and matches exactly one of them, so failing to match says nothing on its own. Check the effective policy if you rely on a nested assertion being enforced.
  • Where a compact policy offers several alternatives within a nested policy, only the first is read.
  • A hardened engine default that WS-SecurityPolicy relaxes - accepting a password-less UsernameToken for an sp:NoPassword policy, or rsa-1_5 key transport for an AlgorithmSuite that asks for it - has to be decided while the security header is read, before the Body says which operation the message invokes. Where SOAPAction has already selected the operation only that operation’s policy is consulted. Otherwise the relaxation is granted across the endpoint’s operations and reimposed once the operation is known, which rejects the message but, for rsa-1_5, only after the key has been unwrapped.
  • The REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS ("requireSignedEncryptedDataElements") configuration tag is not read by the streaming code and is not enforced by it. The tag is shared between both stacks, and the streaming ConfigurationConverter takes only the tags it implements, so setting it on a streaming endpoint has no effect and reports nothing. A deployment that switches from the DOM interceptors to the streaming ones therefore loses the requirement that every EncryptedData element sits in a signed subtree. Use WS-SecurityPolicy SignedParts and EncryptedParts to state what must be signed and encrypted, and note that they constrain the parts the policy names rather than forbidding an additional EncryptedData elsewhere in the message.