From 0d131ab3e7941e4c0a88514d18236c46f68a7f2d Mon Sep 17 00:00:00 2001 From: Rob Leland Date: Tue, 29 Nov 2022 17:05:23 -0500 Subject: [PATCH] Add ability to override inclusive option in wss4j. The default if not set matches wss4j default value. This allows the value to be set safely and simply in code. This replaces the PR from my free-ice which I no longer have access to. This only differs in that it targets the Jakarta NS. --- .../wss4j2/Wss4jSecurityInterceptor.java | 11 +++ .../SaajWss4jMessageInterceptorSignTest.java | 41 +++++++++ ...jWss4jSecurityInterceptorDefaultsTest.java | 83 +++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jSecurityInterceptorDefaultsTest.java 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