spring-projects/spring-ws

Upgraded spring 5.3 to 6.1, spring-ws 3.1.7 to 4.0.10. Getting "Cannot find SOAP wrapper for element [xenc:EncryptedData: null]"

pdotsenko opened this issue · 11 comments

Java 17, saaj-impl 3.0.3, NON-spring-boot. I have a spring-ws app with message-level security encrypting SOAP Body content. After upgrade getting this error:

2024-02-06 19:42:48,899 (http-nio-8080-exec-5) DEBUG [org.springframework.ws.transport.http.MessageDispatcherServlet] - Failed to complete request: java.lang.IllegalArgumentException: Cannot find SOAP wrapper for element [xenc:EncryptedData: null]
Feb 06, 2024 7:42:48 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [spring-ws] in context with path [/sides-broker-si] threw exception [Request processing failed: java.lang.IllegalArgumentException: Cannot find SOAP wrapper for element [xenc:EncryptedData: null]] with root cause
java.lang.IllegalArgumentException: Cannot find SOAP wrapper for element [xenc:EncryptedData: null]
at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:590)
at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:578)
at com.sun.xml.messaging.saaj.soap.impl.ElementImpl.getFirstChildElement(ElementImpl.java:643)
at com.sun.xml.messaging.saaj.soap.impl.BodyImpl.getPayloadQName(BodyImpl.java:444)
at com.sun.xml.messaging.saaj.soap.impl.BodyImpl.hasFault(BodyImpl.java:156)
at org.springframework.ws.soap.saaj.SaajSoapBody.hasFault(SaajSoapBody.java:56)
at org.springframework.ws.soap.AbstractSoapMessage.hasFault(AbstractSoapMessage.java:62)
at org.springframework.ws.soap.AbstractSoapMessage.getFaultCode(AbstractSoapMessage.java:68)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:94)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:60)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:288)
...

This looks similar to #1193, but fixes for that issue don't apply to my environment - I am not running tests, the app is running on Tomcat 10.1.

I would appreciate any help or suggestions. Thanks

@pdotsenko Which other dependencies to you use apart from spring-ws:4.0.10?

@pdotsenko Can you identify any test case that may need to be extended to apply to your usage?

@corneil,

My dependencies include spring-ws-security:4.0.10. My endpoint extends AbstractStaxStreamPayloadEndpoint which is deprecated, but I couldn't replace with equivalent "@endpoint" style setup. I use org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor for securing both request and response with "Timestamp Signature Encrypt" actions. I noticed that when I disable the Wss4jSecurityInterceptor, the error goes away.

I think spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorEncryptionTestCase.java could be extended to reproduce this issue, but I haven't attempted this.

I put together a small sample to reproduce the problem:
https://github.com/pdotsenko/echo-server/ (server)
and
https://github.com/pdotsenko/echo-spring-ws-client/ (client).

The sample is based on spring-ws-samples echo sample modified to fit my use case.

Thanks for looking at this!

Does someone know where this is coming from?
I face the excat same error message but with a less sophisticated technology stack:

  • Tomcat 9.0
  • JDK 17
  • spring 5.3.32
  • spring-ws and spring-ws-security 3.1.8

It's hard to debug but my IDE indeed shows that in the user data of the document there is no reference to the EncryptedData node at the line where it is required here (com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl):

final javax.xml.soap.Node found = (javax.xml.soap.Node) node.getUserData(SAAJ_NODE);
if (found == null && required) {
    throw new IllegalArgumentException(MessageFormat.format("Cannot find SOAP wrapper for element {0}", node));
}

This in turn causes:

java.lang.IllegalArgumentException: Cannot find SOAP wrapper for element [xenc:EncryptedData: null]
	at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:590)
	at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:578)
	at com.sun.xml.messaging.saaj.soap.impl.ElementImpl.getFirstChildElement(ElementImpl.java:660)
	at com.sun.xml.messaging.saaj.soap.impl.BodyImpl.getPayloadQName(BodyImpl.java:439)
	at com.sun.xml.messaging.saaj.soap.impl.BodyImpl.hasFault(BodyImpl.java:151)
	at org.springframework.ws.soap.saaj.SaajSoapBody.hasFault(SaajSoapBody.java:55)
	at org.springframework.ws.soap.AbstractSoapMessage.hasFault(AbstractSoapMessage.java:62)
	at org.springframework.ws.soap.AbstractSoapMessage.getFaultCode(AbstractSoapMessage.java:68)
	at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:94)

@goetzseb and @corneil - I determined that this error is caused by saaj-impl:1.5.3 and happens in both spring 5 and spring 6. I initially thought that the upgrade to spring 6 caused the bug, but was wrong. In spring 5 I was using Axiom soap message factory, but had to switch to Saaj since Axiom is not supported in spring 6 (does not support jakarta XML packages and is not maintained). When I tried using Saaj message factory in a working spring 5 application I received the same error as above.

@goetzseb, you can still use Axiom in Spring 5. Just switch to Axiom soap message factory and it should fix the problem - no other changes should be necessary:

	<bean id="messageFactory"
		class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
	</bean>

or
@bean
public AxiomSoapMessageFactory messageFactory() {
return new AxiomSoapMessageFactory();
}

I am using axiom-impl: 1.2.22.

@pdotsenko thank you for sharing your thoughts.
We are using SAAJ as well and switching to Axoim does not seem to be a viable option for us. After some try and error I came across that downgrading saaj-impl to 1.3.28 works for my environment. Starting with 1.4.0 I keep getting the above mentioned exception.

EDIT:
I think the breaking change is in com.sun.xml.messaging.saaj.soap.impl.ElementImpl.getFirstChildElement(). Till 1.3.28 this simply returned the first child org.w3c.dom.Element of the body part. Starting with 1.4.0 there is an additional call to (SOAPElement) soapDocument.find(eachChild) which I don't know what it does. But it looks into the document node's userData and finds nothing. I cannot tell whether this is an issue caused by spring-ws or saaj-impl.

Thank you @goetzseb for the workaround for spring 5, I can confirm that my project with spring 5/spring-ws 3.1.8 works after downgrading to saaj-impl 1.3.8, although we prefer Axiom for now.
This is still a problem with spring-ws 4 and spring 6 since it is not compatible with saaj-impl 1.3.8.

You cannot have code that stradles the Jakarta EE / Java EE boundary. The javax.xml is only in Java EE and Javakarta EE uses jakarta.xml.
So you have to use either Spring Framework 6.x and Spring WS 4.x or Spring Framework 5.x and Spring WS 3.x

@corneil @pdotsenko @goetzseb I've run in the exact same error.

    springVersion = "6.1.11"
    springWSVersion = "4.0.11"

com.sun.xml.messaging.saaj:saaj-impl:3.0.4

Stacktrace:

java.lang.IllegalArgumentException: Cannot find SOAP wrapper for element [xenc:EncryptedData: null]
at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:590)
at com.sun.xml.messaging.saaj.soap.SOAPDocumentImpl.find(SOAPDocumentImpl.java:578)

I would like to keep using the SAAJ. Does anybody have any tips on how to overcome this problem?

Ok, for me it turned out to be some kind of lazy loading problem. I resolved by adding the 2nd line below:

super.secureMessage(soapMessage, messageContext);
soapMessage.getDocument().getChildNodes(); // <-- trigger initialization/population of elements (deferred by lazy loading)

@d-casal - Your suggestion fixed my issue as well, thanks for sharing!
I updated my sample to reproduce the issue with the workaround fix if anyone wants to look here: https://github.com/pdotsenko/echo-server
@corneil: - This is an on-going issue with spring 6.1.13 and spring-ws 4.0.11, no jakarta/javax boundary is crossed. Although the underlying problem is probably in SAAJ, imho the workaround fix should be added to org.springframework.ws.soap.security.wss4j2).Wss4jSecurityInterceptor.secureMessage(...)