This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 3_0_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/3_0_x-fixes by this push:
new 041af7c49 Fix bug with signature policy validation for STR Transform
(#691)
041af7c49 is described below
commit 041af7c494782247e22f7056b0f76d4a0ff48955
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Tue Sep 15 17:26:44 2026 +0100
Fix bug with signature policy validation for STR Transform (#691)
---
.../stax/test/STRTransformSignedPartsTest.java | 183 +++++++++++++++++++++
.../WSSSignatureReferenceVerifyInputProcessor.java | 24 ++-
2 files changed, 203 insertions(+), 4 deletions(-)
diff --git
a/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/STRTransformSignedPartsTest.java
b/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/STRTransformSignedPartsTest.java
new file mode 100644
index 000000000..b9d94b9e7
--- /dev/null
+++
b/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/STRTransformSignedPartsTest.java
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.wss4j.policy.stax.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.wss4j.common.WSEncryptionPart;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.builder.SAML1Constants;
+import org.apache.wss4j.common.token.SecurityTokenReference;
+import org.apache.wss4j.dom.WSConstants;
+import org.apache.wss4j.dom.engine.WSSConfig;
+import org.apache.wss4j.dom.message.WSSecDKSign;
+import org.apache.wss4j.dom.message.WSSecHeader;
+import org.apache.wss4j.dom.util.WSSecurityUtil;
+import org.apache.wss4j.policy.stax.enforcer.PolicyEnforcer;
+import org.apache.wss4j.policy.stax.enforcer.PolicyInputProcessor;
+import org.apache.wss4j.stax.ext.WSSSecurityProperties;
+import org.apache.wss4j.stax.test.CallbackHandlerImpl;
+import org.apache.wss4j.stax.test.saml.SAML1CallbackHandler;
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class STRTransformSignedPartsTest extends AbstractPolicyTestBase {
+
+ private static final String WSDL_NS = "http://schemas.xmlsoap.org/wsdl/";
+
+ /**
+ * The message signs the SOAP Body and, via an STR-Transform, a SAML
Assertion. The (unsigned)
+ * wsdl:definitions SOAP header of the test message follows the security
header, so the policy
+ * enforcer must still flag it as not signed.
+ */
+ @Test
+ public void testSTRTransformDoesNotDisableSignedPartsEnforcement() throws
Exception {
+
+ String policyString =
+ "<sp:SignedParts
xmlns:sp=\"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702\">\n"
+ + "<sp:Body/>\n"
+ + "<sp:Header Name=\"definitions\" Namespace=\"" +
WSDL_NS + "\"/>\n"
+ + "</sp:SignedParts>";
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
+ callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
+
callbackHandler.setConfirmationMethod(SAML1Constants.CONF_SENDER_VOUCHES);
+ callbackHandler.setIssuer("www.example.com");
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper samlAssertion = new
SamlAssertionWrapper(samlCallback);
+
+ InputStream sourceDocument =
+
this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
+ Document doc =
documentBuilderFactory.newDocumentBuilder().parse(sourceDocument);
+
+ WSSecHeader secHeader = new WSSecHeader(doc);
+ secHeader.insertSecurityHeader();
+
+ SecurityTokenReference secRefSaml = createSamlSTR(doc, samlAssertion,
WSSConfig.getNewInstance());
+
secHeader.getSecurityHeaderElement().appendChild(samlAssertion.toDOM(doc));
+
secHeader.getSecurityHeaderElement().appendChild(secRefSaml.getElement());
+
+ WSSecDKSign sigBuilder = createDKSign(doc, secRefSaml, secHeader);
+ Crypto crypto =
CryptoFactory.getInstance("transmitter-crypto.properties");
+ java.security.Key key = crypto.getPrivateKey("transmitter", "default");
+ Document securedDocument = sigBuilder.build(key.getEncoded());
+
+ javax.xml.transform.Transformer transformer =
TRANSFORMER_FACTORY.newTransformer();
+ transformer.transform(new DOMSource(securedDocument), new
StreamResult(baos));
+
+ WSSSecurityProperties inSecurityProperties = new
WSSSecurityProperties();
+ inSecurityProperties.setCallbackHandler(new CallbackHandlerImpl());
+ inSecurityProperties.loadSignatureVerificationKeystore(
+ this.getClass().getClassLoader().getResource("receiver.jks"),
"default".toCharArray());
+ inSecurityProperties.loadDecryptionKeystore(
+
this.getClass().getClassLoader().getResource("transmitter.jks"),
"default".toCharArray());
+
+ PolicyEnforcer policyEnforcer =
buildAndStartPolicyEngine(policyString);
+ inSecurityProperties.addInputProcessor(new
PolicyInputProcessor(policyEnforcer, inSecurityProperties));
+
+ try {
+ doInboundSecurity(inSecurityProperties, new
ByteArrayInputStream(baos.toByteArray()), policyEnforcer);
+ fail("Expected XMLStreamException");
+ } catch (XMLStreamException e) {
+ Throwable throwable = e.getCause();
+ assertNotNull(throwable);
+ assertTrue(throwable instanceof WSSecurityException);
+ assertEquals("Element /{" + WSConstants.URI_SOAP11_ENV +
"}Envelope"
+ + "/{" + WSConstants.URI_SOAP11_ENV + "}Header"
+ + "/{" + WSDL_NS + "}definitions must be signed",
+ throwable.getMessage());
+ assertEquals(WSSecurityException.INVALID_SECURITY,
((WSSecurityException) throwable).getFaultCode());
+ }
+ }
+
+ /**
+ * Create a SecurityTokenReference to a SAML Assertion
+ */
+ private SecurityTokenReference createSamlSTR(
+ Document doc, SamlAssertionWrapper samlAssertion, WSSConfig
wssConfig
+ ) {
+ SecurityTokenReference secRefSaml = new SecurityTokenReference(doc);
+ String secRefID =
wssConfig.getIdAllocator().createSecureId("STRSAMLId-", secRefSaml);
+ secRefSaml.setID(secRefID);
+
+ org.apache.wss4j.common.token.Reference ref =
+ new org.apache.wss4j.common.token.Reference(doc);
+ ref.setURI("#" + samlAssertion.getId());
+ ref.setValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
+ secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
+ secRefSaml.setReference(ref);
+
+ return secRefSaml;
+ }
+
+ /**
+ * Create a WSSecDKSign object, that signs the SOAP Body as well as the
SAML Assertion
+ * via a STR Transform.
+ */
+ private WSSecDKSign createDKSign(
+ Document doc, SecurityTokenReference secRefSaml, WSSecHeader
secHeader
+ ) throws WSSecurityException {
+ SecurityTokenReference secToken = new SecurityTokenReference(doc);
+ CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+ cryptoType.setAlias("transmitter");
+ Crypto crypto =
CryptoFactory.getInstance("transmitter-crypto.properties");
+ X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
+ secToken.setKeyIdentifierThumb(certs[0]);
+
+ WSSecDKSign sigBuilder = new WSSecDKSign(secHeader);
+ sigBuilder.setStrElem(secToken.getElement());
+ sigBuilder.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
+
+ String soapNamespace =
WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
+ WSEncryptionPart encP =
+ new WSEncryptionPart(WSConstants.ELEM_BODY, soapNamespace,
"Content");
+ sigBuilder.getParts().add(encP);
+
+ encP = new WSEncryptionPart("STRTransform", "", "Element");
+ encP.setId(secRefSaml.getID());
+ Element secRefElement = secRefSaml.getElement();
+ encP.setElement(secRefElement);
+ sigBuilder.getParts().add(encP);
+
+ return sigBuilder;
+ }
+}
diff --git
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
index d2ea9077b..4e77e9cdf 100644
---
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
+++
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
@@ -77,6 +77,7 @@ import
org.apache.xml.security.utils.UnsyncBufferedOutputStream;
public class WSSSignatureReferenceVerifyInputProcessor extends
AbstractSignatureReferenceVerifyInputProcessor {
+ private InternalSignatureReferenceVerifier completedReferenceVerifier;
private boolean replayChecked = false;
public WSSSignatureReferenceVerifyInputProcessor(InputProcessorChain
inputProcessorChain,
@@ -276,8 +277,15 @@ public class WSSSignatureReferenceVerifyInputProcessor
extends AbstractSignature
protected void processElementPath(List<QName> elementPath,
InputProcessorChain inputProcessorChain,
XMLSecEvent xmlSecEvent, ReferenceType
referenceType)
throws XMLSecurityException {
- //fire a SecurityEvent:
final DocumentContext documentContext =
inputProcessorChain.getDocumentContext();
+ if (completedReferenceVerifier != null) {
+ //The verifier never joined the chain, so it won't remove its own
"in signed content" marker:
+ //drop it (re-registering first, in case none was) or
isInSignedContent() stays true forever.
+ documentContext.setIsInSignedContent(-1,
completedReferenceVerifier);
+ documentContext.unsetIsInSignedContent(completedReferenceVerifier);
+ completedReferenceVerifier = null;
+ }
+ //fire a SecurityEvent:
if (elementPath.size() == 3 && WSSUtils.isInSOAPHeader(elementPath)
|| elementPath.size() == 2 &&
WSSUtils.isInSOAPBody(elementPath)) {
SignedPartSecurityEvent signedPartSecurityEvent =
@@ -417,9 +425,12 @@ public class WSSSignatureReferenceVerifyInputProcessor
extends AbstractSignature
throw new
WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN);
}
SecurityTokenReference securityTokenReference =
(SecurityTokenReference) securityToken;
- //todo analyse and fix me: the following statement could be
problematic
- int index =
inputProcessorChain.getProcessors().indexOf(internalSignatureReferenceVerifier);
-
inputProcessorChain.getDocumentContext().setIsInSignedContent(index,
internalSignatureReferenceVerifier);
+ //We are called from the constructor of the verifier, so it is not
part of the processor
+ //chain yet and indexOf() returns -1. The marker is only
registered so that the verifier
+ //has something to remove again when it is done with the buffered
events below.
+ inputProcessorChain.getDocumentContext().setIsInSignedContent(
+
inputProcessorChain.getProcessors().indexOf(internalSignatureReferenceVerifier),
+ internalSignatureReferenceVerifier);
XMLSecStartElement xmlSecStartElement =
securityTokenReference.getXmlSecEvents().getLast().asStartElement();
internalSignatureReferenceVerifier.setStartElement(xmlSecStartElement);
Iterator<XMLSecEvent> xmlSecEventIterator =
securityTokenReference.getXmlSecEvents().descendingIterator();
@@ -430,6 +441,11 @@ public class WSSSignatureReferenceVerifyInputProcessor
extends AbstractSignature
} catch (XMLStreamException e) {
throw new
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
}
+ if (internalSignatureReferenceVerifier.isFinished()) {
+ // Verified from the buffered events above, so the verifier
never joins the chain.
+ // Remember it for processElementPath(), which drops the
marker registered for it.
+ completedReferenceVerifier =
internalSignatureReferenceVerifier;
+ }
}
return parentTransformer;
}