diff --git a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java index 36d5ebcec..566f8a787 100644 --- a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java +++ b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java @@ -179,6 +179,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl private boolean bspCompliant; + private boolean addInclusivePrefixes = true; private boolean securementUseDerivedKey; private CallbackHandler samlCallbackHandler; @@ -541,6 +542,15 @@ public void setBspCompliant(boolean bspCompliant) { this.bspCompliant = bspCompliant; } + /** + * Sets whether to add an InclusiveNamespaces PrefixList as a CanonicalizationMethod child + * when generating Signatures using WSConstants.C14N_EXCL_OMIT_COMMENTS. Default is {@code true}. + */ + public void setAddInclusivePrefixes(boolean addInclusivePrefixes) { + this.handler.setOption(WSHandlerConstants.ADD_INCLUSIVE_PREFIXES, addInclusivePrefixes); + this.addInclusivePrefixes = addInclusivePrefixes; + } + /** * Sets whether the RSA 1.5 key transport algorithm is allowed. */ @@ -676,6 +686,7 @@ protected RequestData initializeValidationRequestData(MessageContext messageCont if (requestData.getBSPEnforcer() != null) { requestData.getBSPEnforcer().setDisableBSPRules(!bspCompliant); } + requestData.setAddInclusivePrefixes(addInclusivePrefixes); // allow for qualified password types for .Net interoperability requestData.setAllowNamespaceQualifiedPasswordTypes(true); diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSignTest.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSignTest.java index 03f33e582..8c90ffbb7 100644 --- a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSignTest.java +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSignTest.java @@ -84,4 +84,45 @@ public void testSignAndValidate() throws Exception { interceptor.validateMessage(message, messageContext); } + + @Test + public void testSignWithoutInclusivePrefixesAndValidate() throws Exception { + + Transformer transformer = TransformerFactoryUtils.newInstance().newTransformer(); + interceptor.setSecurementActions("Signature"); + interceptor.setEnableSignatureConfirmation(false); + interceptor.setSecurementPassword("123456"); + interceptor.setSecurementUsername("rsaKey"); + interceptor.setAddInclusivePrefixes(false); + SOAPMessage saajMessage = saajSoap11MessageFactory.createMessage(); + transformer.transform(new StringSource(PAYLOAD), new DOMResult(saajMessage.getSOAPBody())); + SoapMessage message = new SaajSoapMessage(saajMessage, saajSoap11MessageFactory); + MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(saajSoap11MessageFactory)); + + 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")); + + assertThat(iterator.hasNext()).isTrue(); + + SOAPHeaderElement securityHeader = (SOAPHeaderElement) iterator.next(); + iterator = securityHeader.getChildElements(new QName("http://www.w3.org/2000/09/xmldsig#", "Signature")); + + assertThat(iterator.hasNext()).isTrue(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + message.writeTo(bos); + + MimeHeaders mimeHeaders = new MimeHeaders(); + mimeHeaders.addHeader("Content-Type", "text/xml"); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + + SOAPMessage signed = saajSoap11MessageFactory.createMessage(mimeHeaders, bis); + message = new SaajSoapMessage(signed, saajSoap11MessageFactory); + messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(saajSoap11MessageFactory)); + + interceptor.validateMessage(message, messageContext); + } } diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jSecurityInterceptorDefaultsTest.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jSecurityInterceptorDefaultsTest.java new file mode 100644 index 000000000..a4239f2e3 --- /dev/null +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jSecurityInterceptorDefaultsTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.soap.security.wss4j2; + +import org.apache.wss4j.dom.handler.RequestData; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +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; +import org.springframework.xml.transform.TransformerFactoryUtils; + +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.dom.DOMResult; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.util.AssertionErrors.assertEquals; + +public class SaajWss4jSecurityInterceptorDefaultsTest extends Wss4jTestCase { + + private static final String PAYLOAD = "QQQ"; + + @Test + public void testThatTheDefaultValueForAddInclusivePrefixesMatchesWss4JDefaultValue() { + Wss4jSecurityInterceptor subject = new Wss4jSecurityInterceptor(); + RequestData requestData = new RequestData(); + Boolean springDefault = (Boolean) ReflectionTestUtils.getField(subject, Wss4jSecurityInterceptor.class, "addInclusivePrefixes"); + assertEquals("Spring-ws default for addInclusivePrefixes matches Wss4j default", requestData.isAddInclusivePrefixes(), springDefault); + } + + @Test + public void testThatInitializeValidationRequestDataSetsInclusivePrefixesUsingDefaults() throws TransformerException, SOAPException { + Wss4jSecurityInterceptor subject = new Wss4jSecurityInterceptor(); + + Transformer transformer = TransformerFactoryUtils.newInstance().newTransformer(); + + SOAPMessage saajMessage = saajSoap11MessageFactory.createMessage(); + transformer.transform(new StringSource(PAYLOAD), new DOMResult(saajMessage.getSOAPBody())); + SoapMessage message = new SaajSoapMessage(saajMessage, saajSoap11MessageFactory); + MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(saajSoap11MessageFactory)); + + RequestData validationData = ReflectionTestUtils.invokeMethod(subject, "initializeValidationRequestData", messageContext); + + assertTrue(validationData.isAddInclusivePrefixes()); + } + + @Test + public void testThatInitializeValidationRequestDataSetsInclusivePrefixesUsingNotUsingInclusivePrefixes() throws TransformerException, SOAPException { + Wss4jSecurityInterceptor subject = new Wss4jSecurityInterceptor(); + subject.setAddInclusivePrefixes(false); + Transformer transformer = TransformerFactoryUtils.newInstance().newTransformer(); + + SOAPMessage saajMessage = saajSoap11MessageFactory.createMessage(); + transformer.transform(new StringSource(PAYLOAD), new DOMResult(saajMessage.getSOAPBody())); + SoapMessage message = new SaajSoapMessage(saajMessage, saajSoap11MessageFactory); + MessageContext messageContext = new DefaultMessageContext(message, new SaajSoapMessageFactory(saajSoap11MessageFactory)); + + RequestData validationData = ReflectionTestUtils.invokeMethod(subject, "initializeValidationRequestData", messageContext); + + assertFalse(validationData.isAddInclusivePrefixes()); + } +} \ No newline at end of file