diff --git a/core/src/main/java/org/springframework/ws/context/DefaultMessageContext.java b/core/src/main/java/org/springframework/ws/context/DefaultMessageContext.java index d4085322..cd76af54 100644 --- a/core/src/main/java/org/springframework/ws/context/DefaultMessageContext.java +++ b/core/src/main/java/org/springframework/ws/context/DefaultMessageContext.java @@ -82,6 +82,10 @@ public class DefaultMessageContext extends AbstractMessageContext { response = messageFactory.createWebServiceMessage(inputStream); } + public WebServiceMessageFactory getMessageFactory() { + return messageFactory; + } + private void checkForResponse() throws IllegalStateException { if (response != null) { throw new IllegalStateException("Response message already created"); diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java index 57bf1de8..5cf06444 100755 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java @@ -16,6 +16,8 @@ package org.springframework.ws.soap.security.wss4j; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.Principal; import java.security.cert.X509Certificate; @@ -23,7 +25,7 @@ import java.util.Vector; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; -import javax.xml.soap.SOAPException; +import javax.xml.soap.MessageFactory; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.SOAPFactory; @@ -44,12 +46,14 @@ import org.w3c.dom.Document; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.axiom.AxiomSoapMessage; import org.springframework.ws.soap.axiom.support.AxiomUtils; import org.springframework.ws.soap.saaj.SaajSoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessageException; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor; import org.springframework.ws.soap.security.WsSecuritySecurementException; import org.springframework.ws.soap.security.WsSecurityValidationException; @@ -440,7 +444,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl } RequestData requestData = initializeRequestData(messageContext); - Document envelopeAsDocument = toDocument(soapMessage); + Document envelopeAsDocument = toDocument(soapMessage, messageContext); try { // In case on signature confirmation with no other securement // action, we need to pass an empty securementActionsVector to avoid @@ -485,7 +489,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl return; } - Document envelopeAsDocument = toDocument(soapMessage); + Document envelopeAsDocument = toDocument(soapMessage, messageContext); // Header processing WSSecurityEngine securityEngine = WSSecurityEngine.getInstance(); @@ -590,16 +594,27 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl } /** Converts the given {@link SoapMessage} into a {@link Document}. */ - private Document toDocument(SoapMessage soapMessage) { + private Document toDocument(SoapMessage soapMessage, MessageContext messageContext) { if (soapMessage instanceof SaajSoapMessage) { - javax.xml.soap.SOAPMessage saajMessage = ((SaajSoapMessage) soapMessage).getSaajMessage(); + SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage; + // return saajSoapMessage.getSaajMessage().getSOAPPart(); // does not work, see SWS-345 + Assert.isInstanceOf(DefaultMessageContext.class, messageContext); + DefaultMessageContext defaultMessageContext = (DefaultMessageContext) messageContext; + Assert.isInstanceOf(SaajSoapMessageFactory.class, defaultMessageContext.getMessageFactory()); + MessageFactory messageFactory = + ((SaajSoapMessageFactory) defaultMessageContext.getMessageFactory()).getMessageFactory(); try { - saajMessage.saveChanges(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + saajSoapMessage.writeTo(bos); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + javax.xml.soap.SOAPMessage saajMessage = + messageFactory.createMessage(saajSoapMessage.getSaajMessage().getMimeHeaders(), bis); + saajSoapMessage.setSaajMessage(saajMessage); + return saajMessage.getSOAPPart(); } - catch (SOAPException ex) { + catch (Exception ex) { throw new SaajSoapMessageException("Could not save changes", ex); } - return saajMessage.getSOAPPart(); } else if (soapMessage instanceof AxiomSoapMessage) { AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage; @@ -612,20 +627,10 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl /** * Replaces the contents of the given {@link SoapMessage} with that of the document parameter. Only required when - * using Axiom, since the document returned by {@link #toDocument(org.springframework.ws.soap.SoapMessage)} is live - * for a {@link SaajSoapMessage}. + * using Axiom, since the document returned by {@link #toDocument} is live for a {@link SaajSoapMessage}. */ private void replaceMessage(SoapMessage soapMessage, Document envelope) { - if (soapMessage instanceof SaajSoapMessage) { - javax.xml.soap.SOAPMessage saajMessage = ((SaajSoapMessage) soapMessage).getSaajMessage(); - try { - saajMessage.saveChanges(); - } - catch (SOAPException ex) { - throw new SaajSoapMessageException("Could not save changes", ex); - } - } - else if (soapMessage instanceof AxiomSoapMessage) { + if (soapMessage instanceof AxiomSoapMessage) { // construct a new Axiom message with the processed envelope AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage; SOAPEnvelope envelopeFromDOMDocument = AxiomUtils.toEnvelope(envelope); diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java index bcdd05aa..4bc0a446 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java @@ -2,33 +2,49 @@ package org.springframework.ws.soap.security.wss4j; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.Iterator; import javax.xml.namespace.QName; -import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeaders; +import javax.xml.soap.SOAPHeader; +import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; +import org.springframework.xml.transform.StringSource; public class SaajWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTestCase { + private static final String PAYLOAD = + "QQQ"; + public void testSignAndValidate() throws Exception { - MessageFactory messageFactory = MessageFactory.newInstance(); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); interceptor.setSecurementActions("Signature"); interceptor.setEnableSignatureConfirmation(false); interceptor.setSecurementPassword("123456"); interceptor.setSecurementUsername("rsaKey"); SOAPMessage saajMessage = messageFactory.createMessage(); - saajMessage.getSOAPBody() - .addBodyElement(new QName("http://fabrikam123.com/payloads", "StockSymbol", "tru")).addTextNode("QQQ"); + transformer.transform(new StringSource(PAYLOAD), new DOMResult(saajMessage.getSOAPBody())); SoapMessage message = new SaajSoapMessage(saajMessage); MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(messageFactory)); interceptor.secureMessage(message, messageContext); + SOAPHeader header = ((SaajSoapMessage) message).getSaajMessage().getSOAPHeader(); + Iterator iterator = header.getChildElements(new QName( + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security")); + assertTrue("No security header", iterator.hasNext()); + SOAPHeaderElement securityHeader = (SOAPHeaderElement) iterator.next(); + iterator = securityHeader.getChildElements(new QName("http://www.w3.org/2000/09/xmldsig#", "Signature")); + assertTrue("No signature header", iterator.hasNext()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); message.writeTo(bos);