You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by co...@apache.org on 2026/09/21 11:23:31 UTC
(ws-wss4j) branch 2_4_x-fixes updated (dc72a6adb -> 1b642a735)
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a change to branch 2_4_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
from dc72a6adb Fix SAML Conditions parsing (#721)
new 5aba725a8 Revert "Fix SenderVouches EncryptedKey case (#718)"
new 1b642a735 Revert "Align StaX sender vouches with DOM layer (#720)"
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/wss4j/dom/saml/DOMSAMLUtil.java | 35 -----
.../SamlSenderVouchesVouchingIdentityTest.java | 170 ---------------------
.../processor/input/SAMLTokenInputHandler.java | 46 +-----
.../processor/input/SenderVouchesIdentityTest.java | 137 -----------------
4 files changed, 1 insertion(+), 387 deletions(-)
delete mode 100644 ws-security-dom/src/test/java/org/apache/wss4j/dom/saml/SamlSenderVouchesVouchingIdentityTest.java
delete mode 100644 ws-security-stax/src/test/java/org/apache/wss4j/stax/impl/processor/input/SenderVouchesIdentityTest.java
(ws-wss4j) 01/02: Revert "Fix SenderVouches EncryptedKey case (#718)"
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 2_4_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 5aba725a8144aba4e04e1b588afcdbaa7d591460
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Sep 21 12:23:14 2026 +0100
Revert "Fix SenderVouches EncryptedKey case (#718)"
This reverts commit 1e8eb1f29039fcaaabbc8abd3c930e30c4709a79.
---
.../org/apache/wss4j/dom/saml/DOMSAMLUtil.java | 35 -----
.../SamlSenderVouchesVouchingIdentityTest.java | 170 ---------------------
2 files changed, 205 deletions(-)
diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java
index 30bda991c..68be6eabd 100644
--- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java
+++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java
@@ -242,26 +242,6 @@ public final class DOMSAMLUtil {
return true;
}
-
- /**
- * Whether a signature result identifies the sender that produced it, as opposed to merely
- * proving possession of a key.
- *
- * @param signedResult a SIGN or UT_SIGN result
- * @return true if a trust decision was taken on the signing credential, or the signature was
- * made with a key derived from a UsernameToken whose password was verified
- */
- private static boolean establishesSenderIdentity(WSSecurityEngineResult signedResult) {
- if (Boolean.TRUE.equals(signedResult.get(WSSecurityEngineResult.TAG_VALIDATED_TOKEN))) {
- return true;
- }
-
- // A UsernameToken derived key is not stamped as a validated token - no Validator runs on
- // the signing credential itself - but the token's password was verified before the key was
- // derived from it, so the sender is authenticated all the same.
- Integer action = (Integer)signedResult.get(WSSecurityEngineResult.TAG_ACTION);
- return action != null && WSConstants.UT_SIGN == action.intValue();
- }
/**
* Return true if there is a signature which references the Assertion and the SOAP Body.
* @param assertionWrapper the SamlAssertionWrapper object
@@ -274,22 +254,7 @@ public final class DOMSAMLUtil {
Element body,
List<WSSecurityEngineResult> signed
) {
- // A sender-vouches assertion is only worth as much as the identity of whoever vouched for
- // it. An assertion that is itself signed carries that backing already - the signature was
- // trust-verified against its issuer (see SamlAssertionValidator.verifySignedAssertion) -
- // and the message signature merely binds it to this message, so any signature will do.
- //
- // An unsigned assertion has no such backing. The only party asserting it is whoever signed
- // the message, so that signature has to have been made with a credential whose identity was
- // actually established. A signature verified with a bare symmetric key - one taken from an
- // EncryptedKey that the sender minted for itself, say - proves possession of that key and
- // nothing whatsoever about who sent it, and so cannot vouch for anybody.
- boolean vouchingIdentityRequired = !assertionWrapper.isSigned();
-
for (WSSecurityEngineResult signedResult : signed) {
- if (vouchingIdentityRequired && !establishesSenderIdentity(signedResult)) {
- continue;
- }
@SuppressWarnings("unchecked")
List<WSDataRef> sl =
(List<WSDataRef>)signedResult.get(
diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/saml/SamlSenderVouchesVouchingIdentityTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/saml/SamlSenderVouchesVouchingIdentityTest.java
deleted file mode 100644
index b5c22567d..000000000
--- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/saml/SamlSenderVouchesVouchingIdentityTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * 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.dom.saml;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.wss4j.common.crypto.Crypto;
-import org.apache.wss4j.common.crypto.CryptoFactory;
-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.util.SOAPUtil;
-import org.apache.wss4j.dom.WSConstants;
-import org.apache.wss4j.dom.WSDataRef;
-import org.apache.wss4j.dom.common.SAML1CallbackHandler;
-import org.apache.wss4j.dom.engine.WSSConfig;
-import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
-import org.apache.wss4j.dom.util.WSSecurityUtil;
-import org.junit.jupiter.api.Test;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * A sender-vouches assertion is a claim made by whoever sent the message, so it is worth exactly as
- * much as that sender's identity. An assertion that is itself signed carries its issuer's signature,
- * which is trust-verified separately; an unsigned one does not, and the only thing asserting it is
- * the signature over the message. That signature therefore has to have been made with a credential
- * whose identity was established - not merely with a key the sender happens to hold, such as one
- * taken from an EncryptedKey the sender minted for itself.
- */
-public class SamlSenderVouchesVouchingIdentityTest {
-
- private final Crypto crypto;
-
- public SamlSenderVouchesVouchingIdentityTest() throws Exception {
- WSSConfig.init();
- crypto = CryptoFactory.getInstance();
- }
-
- /**
- * The shape of the bypass: an unsigned sender-vouches assertion, and a signature that covers it
- * and the Body but was verified with a bare symmetric key, so it says nothing about who sent it.
- */
- @Test
- public void testUnsignedAssertionRejectsUnidentifiedSigner() throws Exception {
- Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
- SamlAssertionWrapper assertion = senderVouchesAssertion(doc, false);
- Element body = WSSecurityUtil.findBodyElement(doc);
-
- List<WSSecurityEngineResult> signed = Collections.singletonList(
- coveringSignature(WSConstants.SIGN, false, assertion.getElement(), body));
-
- assertFalse(DOMSAMLUtil.checkSenderVouches(assertion, null, body, signed),
- "An unsigned sender-vouches assertion must not be accepted on the word of a signer "
- + "whose identity was never established");
- }
-
- /**
- * The same coverage from a credential a Validator made a trust decision about is what
- * sender-vouches is supposed to look like.
- */
- @Test
- public void testUnsignedAssertionAcceptsTrustValidatedSigner() throws Exception {
- Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
- SamlAssertionWrapper assertion = senderVouchesAssertion(doc, false);
- Element body = WSSecurityUtil.findBodyElement(doc);
-
- List<WSSecurityEngineResult> signed = Collections.singletonList(
- coveringSignature(WSConstants.SIGN, true, assertion.getElement(), body));
-
- assertTrue(DOMSAMLUtil.checkSenderVouches(assertion, null, body, signed));
- }
-
- /**
- * A UsernameToken derived key is never stamped as a validated token, but deriving it required
- * the password, so the sender is authenticated and may vouch.
- */
- @Test
- public void testUnsignedAssertionAcceptsUsernameTokenSigner() throws Exception {
- Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
- SamlAssertionWrapper assertion = senderVouchesAssertion(doc, false);
- Element body = WSSecurityUtil.findBodyElement(doc);
-
- List<WSSecurityEngineResult> signed = Collections.singletonList(
- coveringSignature(WSConstants.UT_SIGN, false, assertion.getElement(), body));
-
- assertTrue(DOMSAMLUtil.checkSenderVouches(assertion, null, body, signed));
- }
-
- /**
- * An assertion signed by its issuer already carries a vouching identity of its own - the
- * message signature only binds it to this message - so the symmetric-binding deployments that
- * rely on that pattern must keep working.
- */
- @Test
- public void testSignedAssertionAcceptsAnySigner() throws Exception {
- Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
- SamlAssertionWrapper assertion = senderVouchesAssertion(doc, true);
- assertTrue(assertion.isSigned(), "precondition: the assertion is signed");
- Element body = WSSecurityUtil.findBodyElement(doc);
-
- List<WSSecurityEngineResult> signed = Collections.singletonList(
- coveringSignature(WSConstants.SIGN, false, assertion.getElement(), body));
-
- assertTrue(DOMSAMLUtil.checkSenderVouches(assertion, null, body, signed));
- }
-
- private SamlAssertionWrapper senderVouchesAssertion(Document doc, boolean sign) throws Exception {
- 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);
- if (sign) {
- samlAssertion.signAssertion("16c73ab6-b892-458f-abf5-2f875f74882e", "security",
- crypto, false);
- }
- samlAssertion.toDOM(doc);
- return samlAssertion;
- }
-
- /**
- * A signature result covering the given elements. {@code validated} is what
- * SignatureProcessor stamps when a trust decision was actually taken on the signing credential.
- */
- private WSSecurityEngineResult coveringSignature(
- int action, boolean validated, Element... protectedElements
- ) {
- List<WSDataRef> dataRefs = new ArrayList<>();
- for (Element protectedElement : protectedElements) {
- WSDataRef dataRef = new WSDataRef();
- dataRef.setProtectedElement(protectedElement);
- dataRefs.add(dataRef);
- }
-
- WSSecurityEngineResult result = new WSSecurityEngineResult(action);
- result.put(WSSecurityEngineResult.TAG_DATA_REF_URIS, dataRefs);
- if (validated) {
- result.put(WSSecurityEngineResult.TAG_VALIDATED_TOKEN, Boolean.TRUE);
- }
- return result;
- }
-}
(ws-wss4j) 02/02: Revert "Align StaX sender vouches with DOM layer (#720)"
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 2_4_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 1b642a7359bf4d6ffc2680b6d7a7511d00040db1
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Sep 21 12:23:23 2026 +0100
Revert "Align StaX sender vouches with DOM layer (#720)"
This reverts commit 00472e244b6bb75d7747fdf6d66523b03985763c.
---
.../processor/input/SAMLTokenInputHandler.java | 46 +------
.../processor/input/SenderVouchesIdentityTest.java | 137 ---------------------
2 files changed, 1 insertion(+), 182 deletions(-)
diff --git a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/SAMLTokenInputHandler.java b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/SAMLTokenInputHandler.java
index 3e87ee675..e70918816 100644
--- a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/SAMLTokenInputHandler.java
+++ b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/SAMLTokenInputHandler.java
@@ -527,40 +527,6 @@ public class SAMLTokenInputHandler extends AbstractInputSecurityHeaderHandler {
* which can not be done until the whole soap-header is processed and we know that the whole soap-body
* is signed.
*/
- /**
- * Whether a security token identifies the sender that signed with it, as opposed to merely
- * proving possession of a key.
- *
- * A certificate or a public key was checked against the receiver's own truststore when the
- * token was verified - see X509SecurityTokenImpl#verify and
- * RsaKeyValueSecurityTokenImpl#verify, both of which call Crypto#verifyTrust. A bare symmetric
- * key was not, and could not be: an EncryptedKey that the sender minted for itself under the
- * receiver's public certificate proves possession of a key the sender chose, and nothing
- * whatsoever about who sent it.
- *
- * Two symmetric cases do carry an identity. A key derived from a UsernameToken required the
- * password to derive, and a Kerberos session key came out of a ticket the KDC issued to a
- * named client. These are the streaming counterparts of the results the DOM engine stamps
- * with TAG_VALIDATED_TOKEN, plus its UT_SIGN case - see DOMSAMLUtil#establishesSenderIdentity.
- *
- * The token is resolved to the root of its key wrapping chain first, so that a
- * DerivedKeyToken is judged on whatever it was derived from.
- */
- static boolean establishesSenderIdentity(SecurityToken securityToken) throws XMLSecurityException {
- SecurityToken rootToken = WSSUtils.getRootToken(securityToken);
-
- X509Certificate[] x509Certificates = rootToken.getX509Certificates();
- if (x509Certificates != null && x509Certificates.length > 0) {
- return true;
- }
- if (rootToken.getPublicKey() != null) {
- return true;
- }
-
- return WSSecurityTokenConstants.USERNAME_TOKEN.equals(rootToken.getTokenType())
- || WSSecurityTokenConstants.KERBEROS_TOKEN.equals(rootToken.getTokenType());
- }
-
static class SAMLTokenVerifierInputProcessor extends AbstractInputProcessor implements SecurityEventListener {
private SamlAssertionWrapper samlAssertionWrapper;
@@ -744,20 +710,10 @@ public class SAMLTokenInputHandler extends AbstractInputSecurityHeaderHandler {
samlTokenSignedElementSecurityEvent = signedElementSecurityEvent;
}
}
- // A sender-vouches assertion is only worth as much as the identity of
- // whoever vouched for it. An assertion that is itself signed carries that
- // backing already, and the message signature merely binds it to this
- // message, so any signature will do. An unsigned assertion has no such
- // backing: the only party asserting it is whoever signed the message, so
- // that signature has to have been made with a credential whose identity
- // was actually established.
if (bodySignedPartSecurityEvent != null
&& samlTokenSignedElementSecurityEvent != null
&& bodySignedPartSecurityEvent.getSecurityToken()
- == samlTokenSignedElementSecurityEvent.getSecurityToken()
- && (samlAssertionWrapper.isSigned()
- || establishesSenderIdentity(
- bodySignedPartSecurityEvent.getSecurityToken()))) {
+ == samlTokenSignedElementSecurityEvent.getSecurityToken()) {
return;
}
methodNotSatisfied = true;
diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/impl/processor/input/SenderVouchesIdentityTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/impl/processor/input/SenderVouchesIdentityTest.java
deleted file mode 100644
index 30b4d7b97..000000000
--- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/impl/processor/input/SenderVouchesIdentityTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * 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.stax.impl.processor.input;
-
-import java.security.KeyStore;
-import java.security.cert.X509Certificate;
-
-import org.apache.wss4j.stax.impl.securityToken.EncryptedKeySha1SecurityTokenImpl;
-import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
-import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * An unsigned sender-vouches assertion is only as good as the identity of whoever signed the
- * message that carries it. These are the cases SAMLTokenInputHandler#establishesSenderIdentity
- * has to separate: a credential the receiver made a trust decision about, against a bare
- * symmetric key that says nothing about who sent the message.
- */
-public class SenderVouchesIdentityTest {
-
- /**
- * The bypass: an EncryptedKey the sender minted for itself under the receiver's public
- * certificate. The HMAC signature it keys verifies perfectly and identifies nobody.
- */
- @Test
- public void testEncryptedKeyDoesNotVouch() throws Exception {
- assertFalse(SAMLTokenInputHandler.establishesSenderIdentity(
- token(WSSecurityTokenConstants.EncryptedKeyToken)));
- }
-
- /**
- * The same key reached through a DerivedKeyToken. The derived token is judged on what it was
- * derived from, so this must not become a way round the previous case.
- */
- @Test
- public void testKeyDerivedFromAnEncryptedKeyDoesNotVouch() throws Exception {
- EncryptedKeySha1SecurityTokenImpl derivedKey = token(WSSecurityTokenConstants.DerivedKeyToken);
- derivedKey.setKeyWrappingToken(token(WSSecurityTokenConstants.EncryptedKeyToken));
-
- assertFalse(SAMLTokenInputHandler.establishesSenderIdentity(derivedKey));
- }
-
- /**
- * A certificate was checked against the receiver's truststore when the token was verified.
- */
- @Test
- public void testCertificateVouches() throws Exception {
- EncryptedKeySha1SecurityTokenImpl securityToken = token(WSSecurityTokenConstants.X509V3Token);
- securityToken.setX509Certificates(new X509Certificate[] {transmitterCertificate()});
-
- assertTrue(SAMLTokenInputHandler.establishesSenderIdentity(securityToken));
- }
-
- /**
- * So was a bare public key - see RsaKeyValueSecurityTokenImpl#verify.
- */
- @Test
- public void testPublicKeyVouches() throws Exception {
- EncryptedKeySha1SecurityTokenImpl securityToken = token(WSSecurityTokenConstants.KeyValueToken);
- securityToken.setPublicKey(transmitterCertificate().getPublicKey());
-
- assertTrue(SAMLTokenInputHandler.establishesSenderIdentity(securityToken));
- }
-
- /**
- * A UsernameToken derived key carries no credential for a trust decision, but deriving it
- * required the password, so the sender is authenticated all the same. This is the streaming
- * counterpart of the DOM engine's UT_SIGN case.
- */
- @Test
- public void testUsernameTokenVouches() throws Exception {
- assertTrue(SAMLTokenInputHandler.establishesSenderIdentity(
- token(WSSecurityTokenConstants.USERNAME_TOKEN)));
- }
-
- /**
- * A Kerberos session key came out of a ticket the KDC issued to a named client.
- */
- @Test
- public void testKerberosTokenVouches() throws Exception {
- assertTrue(SAMLTokenInputHandler.establishesSenderIdentity(
- token(WSSecurityTokenConstants.KERBEROS_TOKEN)));
- }
-
- /**
- * A token carrying only a symmetric key of some other provenance - a SecurityContextToken,
- * say - is no better placed to vouch than an EncryptedKey is.
- */
- @Test
- public void testOtherSymmetricTokenDoesNotVouch() throws Exception {
- assertFalse(SAMLTokenInputHandler.establishesSenderIdentity(
- token(WSSecurityTokenConstants.SECURITY_CONTEXT_TOKEN)));
- }
-
- /**
- * A token of the given type carrying nothing else. EncryptedKeySha1SecurityTokenImpl is used
- * only because it is a concrete inbound token whose credentials can be set from a test; what
- * is under test is the rule, not the class.
- */
- private EncryptedKeySha1SecurityTokenImpl token(SecurityTokenConstants.TokenType tokenType) {
- return new EncryptedKeySha1SecurityTokenImpl(null, null, "sha1-identifier", "token-id") {
- @Override
- public SecurityTokenConstants.TokenType getTokenType() {
- return tokenType;
- }
- };
- }
-
- private X509Certificate transmitterCertificate() throws Exception {
- KeyStore keyStore = KeyStore.getInstance("jks");
- keyStore.load(this.getClass().getClassLoader().getResourceAsStream("transmitter.jks"),
- "default".toCharArray());
- X509Certificate certificate = (X509Certificate)keyStore.getCertificate("transmitter");
- assertNotNull(certificate, "precondition: the test keystore holds the transmitter certificate");
- return certificate;
- }
-}