You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@axis.apache.org by ro...@apache.org on 2026/06/10 18:09:43 UTC

(axis-axis2-java-rampart) branch master updated (977e51f8 -> b2cce440)

This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git


    from 977e51f8 RAMPART-428: harden the test XML parser against XXE
     new 8ce0aeb0 RAMPART-427: add the CLIENT_SIDE marker parameter race-free
     new c8071bad Address RAMPART-427 review: document init trade-off, guard config parsing
     new e9ccb685 RAMPART-411: add regression test for signing the BinarySecurityToken
     new 205ae0c1 RAMPART-431: skip signing SignedParts headers that are absent from the message
     new 07701083 RAMPART-44 / RAMPART-252: enforce the policy algorithm suite on inbound messages
     new b2cce440 RAMPART-44 / RAMPART-252: add a dedicated algorithm-downgrade test

The 6 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:
 .../java/org/apache/rampart/RampartEngine.java     |  50 +++++++
 .../org/apache/rampart/RampartMessageData.java     |  56 ++++++--
 .../rampart/builder/TransportBindingBuilder.java   |  49 ++++---
 .../resources/org/apache/rampart/errors.properties |   1 +
 .../org/apache/rampart/RampartMessageDataTest.java |  83 +++++++++++
 .../test/java/org/apache/rampart/RampartTest.java  |  10 +-
 .../rampart/AsymmetricBindingBuilderTest.java      |  24 ++++
 .../org/apache/rampart/PolicyAssertionsTest.java   |  37 +++++
 .../rampart/TransportBindingBuilderTest.java       |  23 ++++
 ...ng-1.xml => rampart-asymm-binding-1-sha256.xml} |   4 +-
 ...xml => rampart-asymm-binding-protecttokens.xml} | 152 +++++++++++----------
 ...art-transport-binding-absent-signed-header.xml} | 134 +++++++++---------
 12 files changed, 457 insertions(+), 166 deletions(-)
 create mode 100644 modules/rampart-core/src/test/java/org/apache/rampart/RampartMessageDataTest.java
 copy modules/rampart-tests/test-resources/policy/{rampart-asymm-binding-1.xml => rampart-asymm-binding-1-sha256.xml} (90%)
 copy modules/rampart-tests/test-resources/policy/{rampart-asymm-binding-1.xml => rampart-asymm-binding-protecttokens.xml} (90%)
 copy modules/rampart-tests/test-resources/policy/{rampart-transport-binding.xml => rampart-transport-binding-absent-signed-header.xml} (87%)


(axis-axis2-java-rampart) 03/06: RAMPART-411: add regression test for signing the BinarySecurityToken

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit e9ccb685286663879611feb62daa634ce5fe6267
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jun 10 04:50:04 2026 -1000

    RAMPART-411: add regression test for signing the BinarySecurityToken
    
    The original report (Rampart 1.6.2) could not sign the BinarySecurityToken: the
    WSS4J 1.x parameter-based "signatureParts" config failed with "Element to
    encrypt/sign not found: ...BinarySecurityToken" (the BST was referenced by QName -
    with the wrong namespace - and could not be resolved at signing time).
    
    In 2.0.0 this works through WS-SecurityPolicy: sp:ProtectTokens sets token
    protection and the binding builders add the BinarySecurityToken to the signature
    by its wsu:Id (RampartUtil/...getBSTTokenId), so the token is reliably signed. No
    code change is required.
    
    Adds an AsymmetricBindingBuilderTest case using a new ProtectTokens policy: the
    message builds successfully and the security header contains a signed
    BinarySecurityToken and Signature. Had BST signing been broken, build() would throw
    the original "Element to encrypt/sign not found" error. Verified with a full clean
    -Papache-release verify across all modules including the nine policy samples on
    JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../rampart/AsymmetricBindingBuilderTest.java      | 24 +++++++
 .../policy/rampart-asymm-binding-protecttokens.xml | 77 ++++++++++++++++++++++
 2 files changed, 101 insertions(+)

diff --git a/modules/rampart-tests/src/test/java/org/apache/rampart/AsymmetricBindingBuilderTest.java b/modules/rampart-tests/src/test/java/org/apache/rampart/AsymmetricBindingBuilderTest.java
index a99dd2b0..ce3bc080 100644
--- a/modules/rampart-tests/src/test/java/org/apache/rampart/AsymmetricBindingBuilderTest.java
+++ b/modules/rampart-tests/src/test/java/org/apache/rampart/AsymmetricBindingBuilderTest.java
@@ -47,6 +47,30 @@ public class AsymmetricBindingBuilderTest extends MessageBuilderTestBase {
         this.verifySecHeader(list.iterator(), ctx.getEnvelope());
     }
     
+    public void testAsymmBindingProtectTokens() throws Exception {
+        // RAMPART-411: with sp:ProtectTokens the BinarySecurityToken must be signed.
+        // The original failure was during signing ("Element to encrypt/sign not found:
+        // ...BinarySecurityToken"), so a successful build that produces a BST and a
+        // Signature proves the token is now correctly added to the signature (by its
+        // wsu:Id) and signed.
+        MessageContext ctx = getMsgCtx();
+
+        String policyXml = "test-resources/policy/rampart-asymm-binding-protecttokens.xml";
+        Policy policy = this.loadPolicy(policyXml);
+
+        ctx.setProperty(RampartMessageData.KEY_RAMPART_POLICY, policy);
+
+        MessageBuilder builder = new MessageBuilder();
+        builder.build(ctx);
+
+        ArrayList<QName> list = new ArrayList<QName>();
+        list.add(new QName(WSConstants.WSU_NS, WSConstants.TIMESTAMP_TOKEN_LN));
+        list.add(new QName(WSConstants.WSSE_NS, WSConstants.BINARY_TOKEN_LN));
+        list.add(new QName(WSConstants.SIG_NS, WSConstants.SIG_LN));
+
+        this.verifySecHeader(list.iterator(), ctx.getEnvelope());
+    }
+
     public void testAsymmBindingServerSide() throws Exception {
         MessageContext ctx = getMsgCtx();
         
diff --git a/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-protecttokens.xml b/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-protecttokens.xml
new file mode 100644
index 00000000..94fad611
--- /dev/null
+++ b/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-protecttokens.xml
@@ -0,0 +1,77 @@
+<wsp:Policy  wsu:Id="protect-tokens" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
+	<wsp:ExactlyOne>
+		<wsp:All>
+			<sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:InitiatorToken>
+						<wsp:Policy>
+							<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
+								<wsp:Policy>
+									<sp:WssX509V3Token10/>
+								</wsp:Policy>
+							</sp:X509Token>
+						</wsp:Policy>
+					</sp:InitiatorToken>
+					<sp:RecipientToken>
+						<wsp:Policy>
+							<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
+								<wsp:Policy>
+									<sp:WssX509V3Token10/>
+								</wsp:Policy>
+							</sp:X509Token>
+						</wsp:Policy>
+					</sp:RecipientToken>
+					<sp:AlgorithmSuite>
+						<wsp:Policy>
+							<sp:Basic128/>
+						</wsp:Policy>
+					</sp:AlgorithmSuite>
+					<sp:Layout>
+						<wsp:Policy>
+							<sp:Lax/>
+						</wsp:Policy>
+					</sp:Layout>
+					<sp:IncludeTimestamp/>
+					<!-- RAMPART-411: sign the BinarySecurityToken (the X509 token). -->
+					<sp:ProtectTokens/>
+					<sp:OnlySignEntireHeadersAndBody/>
+				</wsp:Policy>
+			</sp:AsymmetricBinding>
+			<sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:MustSupportRefKeyIdentifier/>
+					<sp:MustSupportRefIssuerSerial/>
+				</wsp:Policy>
+			</sp:Wss10>
+			<sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:MustSupportIssuedTokens/>
+					<sp:RequireClientEntropy/>
+					<sp:RequireServerEntropy/>
+				</wsp:Policy>
+			</sp:Trust10>
+
+			<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
+				<ramp:user>alice</ramp:user>
+				<ramp:encryptionUser>bob</ramp:encryptionUser>
+				<ramp:passwordCallbackClass>org.apache.rampart.TestCBHandler</ramp:passwordCallbackClass>
+
+				<ramp:signatureCrypto>
+					<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.file">test-resources/keys/interop2024.pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
+					</ramp:crypto>
+				</ramp:signatureCrypto>
+				<ramp:encryptionCypto>
+					<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.file">test-resources/keys/interop2024.pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
+					</ramp:crypto>
+				</ramp:encryptionCypto>
+			</ramp:RampartConfig>
+
+		</wsp:All>
+	</wsp:ExactlyOne>
+</wsp:Policy>


(axis-axis2-java-rampart) 06/06: RAMPART-44 / RAMPART-252: add a dedicated algorithm-downgrade test

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit b2cce440a2547a77061fcc5d659999b5f20a055b
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jun 10 08:06:10 2026 -1000

    RAMPART-44 / RAMPART-252: add a dedicated algorithm-downgrade test
    
    Follow-up to the Gemini review: add a focused, deterministic test for the new
    inbound algorithm-suite enforcement, rather than relying only on the incidental
    coverage from RampartTest case 34.
    
    PolicyAssertionsTest.testAlgorithmSuiteDowngradeRejected builds a signed request
    with a Basic128 (SHA-1) policy, then validates it as the server against a
    Basic128Sha256 (SHA-256) policy and asserts the message is rejected - a peer must
    not be able to downgrade the digest algorithm below what the policy requires.
    
    Confirmed the test guards the fix: with applyAlgorithmSuite disabled the SHA-1
    signature verifies and the message is accepted, failing the test. Adds the
    rampart-asymm-binding-1-sha256.xml policy (a Basic128Sha256 copy of
    rampart-asymm-binding-1.xml). Verified with a full clean -Papache-release verify
    across all modules including the nine policy samples on JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../org/apache/rampart/PolicyAssertionsTest.java   | 37 +++++++++++
 .../policy/rampart-asymm-binding-1-sha256.xml      | 75 ++++++++++++++++++++++
 2 files changed, 112 insertions(+)

diff --git a/modules/rampart-tests/src/test/java/org/apache/rampart/PolicyAssertionsTest.java b/modules/rampart-tests/src/test/java/org/apache/rampart/PolicyAssertionsTest.java
index 9ea4c748..ae7fcfd3 100644
--- a/modules/rampart-tests/src/test/java/org/apache/rampart/PolicyAssertionsTest.java
+++ b/modules/rampart-tests/src/test/java/org/apache/rampart/PolicyAssertionsTest.java
@@ -159,4 +159,41 @@ public class PolicyAssertionsTest extends MessageBuilderTestBase {
         }
 
     }
+
+    public void testAlgorithmSuiteDowngradeRejected() throws Exception {
+        // RAMPART-44 / RAMPART-252: a message signed with a weaker algorithm suite
+        // (Basic128, SHA-1) must be rejected when the service policy requires a stronger
+        // suite (Basic128Sha256, SHA-256), so a peer cannot downgrade the digest
+        // algorithm. Without algorithm-suite enforcement the SHA-1 signature would verify
+        // and the message would be accepted.
+        MessageContext ctx = getMsgCtx();
+
+        // Build the request with the SHA-1 (Basic128) policy.
+        Policy buildPolicy = loadPolicy("test-resources/policy/rampart-asymm-binding-1.xml");
+        ctx.setProperty(RampartMessageData.KEY_RAMPART_POLICY, buildPolicy);
+        MessageBuilder builder = new MessageBuilder();
+        builder.build(ctx);
+
+        SOAPBuilder soapBuilder = new SOAPBuilder();
+        SOAPEnvelope env = ctx.getEnvelope();
+        ByteArrayInputStream inStream = new ByteArrayInputStream(env.toString().getBytes());
+        env = (SOAPEnvelope) soapBuilder.processDocument(inStream, "text/xml", ctx);
+        ctx.setEnvelope(env);
+
+        // Validate as the server with the SHA-256 (Basic128Sha256) policy.
+        ctx.setServerSide(true);
+        AxisService axisService = ctx.getAxisService();
+        axisService.removeParameter(axisService.getParameter(RampartMessageData.PARAM_CLIENT_SIDE));
+
+        Policy verifyPolicy = loadPolicy("test-resources/policy/rampart-asymm-binding-1-sha256.xml");
+        ctx.setProperty(RampartMessageData.KEY_RAMPART_POLICY, verifyPolicy);
+
+        RampartEngine engine = new RampartEngine();
+        try {
+            engine.process(ctx);
+            fail("A message signed with SHA-1 must be rejected when the policy requires SHA-256");
+        } catch (Exception expected) {
+            // Expected: algorithm-suite enforcement rejects the weaker digest algorithm.
+        }
+    }
 }
diff --git a/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-1-sha256.xml b/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-1-sha256.xml
new file mode 100644
index 00000000..d0fc48ec
--- /dev/null
+++ b/modules/rampart-tests/test-resources/policy/rampart-asymm-binding-1-sha256.xml
@@ -0,0 +1,75 @@
+<wsp:Policy  wsu:Id="6-sha256" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
+	<wsp:ExactlyOne>
+		<wsp:All>
+			<sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:InitiatorToken>
+						<wsp:Policy>
+							<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
+								<wsp:Policy>
+									<sp:WssX509V3Token10/>
+								</wsp:Policy>
+							</sp:X509Token>
+						</wsp:Policy>
+					</sp:InitiatorToken>
+					<sp:RecipientToken>
+						<wsp:Policy>
+							<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
+								<wsp:Policy>
+									<sp:WssX509V3Token10/>
+								</wsp:Policy>
+							</sp:X509Token>
+						</wsp:Policy>
+					</sp:RecipientToken>
+					<sp:AlgorithmSuite>
+						<wsp:Policy>
+							<sp:Basic128Sha256/>
+						</wsp:Policy>
+					</sp:AlgorithmSuite>
+					<sp:Layout>
+						<wsp:Policy>
+							<sp:Lax/>
+						</wsp:Policy>
+					</sp:Layout>
+					<sp:IncludeTimestamp/>
+					<sp:OnlySignEntireHeadersAndBody/>
+				</wsp:Policy>
+			</sp:AsymmetricBinding>
+			<sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:MustSupportRefKeyIdentifier/>
+					<sp:MustSupportRefIssuerSerial/>
+				</wsp:Policy>
+			</sp:Wss10>
+			<sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+				<wsp:Policy>
+					<sp:MustSupportIssuedTokens/>
+					<sp:RequireClientEntropy/>
+					<sp:RequireServerEntropy/>
+				</wsp:Policy>
+			</sp:Trust10>
+			
+			<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
+				<ramp:user>alice</ramp:user>
+				<ramp:encryptionUser>bob</ramp:encryptionUser>
+				<ramp:passwordCallbackClass>org.apache.rampart.TestCBHandler</ramp:passwordCallbackClass>
+				
+				<ramp:signatureCrypto>
+					<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.file">test-resources/keys/interop2024.pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
+					</ramp:crypto>
+				</ramp:signatureCrypto>
+				<ramp:encryptionCypto>
+					<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.file">test-resources/keys/interop2024.pkcs12</ramp:property>
+						<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
+					</ramp:crypto>
+				</ramp:encryptionCypto>
+			</ramp:RampartConfig>
+	
+		</wsp:All>
+	</wsp:ExactlyOne>
+</wsp:Policy>


(axis-axis2-java-rampart) 02/06: Address RAMPART-427 review: document init trade-off, guard config parsing

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit c8071bad73aa1e66aefcdaa4afffefc262afed27
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jun 10 03:58:00 2026 -1000

    Address RAMPART-427 review: document init trade-off, guard config parsing
    
    Follow-up to the Gemini review of the RAMPART-427 fix (both findings are
    pre-existing in RampartMessageData, not introduced by the race fix):
    
    - Document the performance-vs-correctness trade-off of running the WSS4J /
      Santuario / OpenSAML one-time initialisers in the per-message constructor: the
      calls are idempotent guards kept there to avoid the OpenSAML-5-migration
      unmarshallerFactory ordering bug; moving them to a once-per-lifecycle init is a
      separate, riskier change. No behaviour change.
    - Wrap the timestampTTL / timestampMaxSkew Integer.parseInt calls so a non-numeric
      RampartConfig value throws a clear RampartException naming the offending
      parameter and value, instead of an unhandled NumberFormatException. Adds the
      invalidRampartConfigValue message.
    
    (The reviewer's third, JUnit-version, note is skipped to stay consistent with the
    existing test suite style.) Verified with a full clean -Papache-release verify
    across all modules including the nine policy samples on JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../org/apache/rampart/RampartMessageData.java     | 30 +++++++++++++++++++---
 .../resources/org/apache/rampart/errors.properties |  1 +
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java b/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
index 8ba45a90..66ea13ac 100644
--- a/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
+++ b/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
@@ -236,8 +236,20 @@ public class RampartMessageData {
         
         try {
 
-            // CRITICAL FIX: Initialize WSS4J before creating WSSConfig to ensure OpenSAML integration works
-            // This prevents OpenSAMLUtil.unmarshallerFactory from being null when processing SAML assertions
+            // PERFORMANCE vs CORRECTNESS:
+            // The WSS4J / Santuario / OpenSAML providers below are one-time, process-wide
+            // initialisers. Invoking them in this per-message constructor is intentional but
+            // is a correctness-over-performance trade-off:
+            //   * Correctness: it guarantees the SAML stack is initialised before any
+            //     assertion is processed. Without it OpenSAMLUtil.unmarshallerFactory could be
+            //     null (an initialisation-ordering problem surfaced during the OpenSAML 5 /
+            //     Jakarta migration), causing SAML processing to fail intermittently.
+            //   * Performance: all of these calls are idempotent guards, so the steady-state
+            //     cost is only the guard checks rather than real re-initialisation - but they
+            //     still run on every message, which is wasteful under high throughput.
+            // The proper fix is to run this once per application lifecycle (e.g. in the
+            // Rampart/Rahas module init), which is tracked separately; do not move it without
+            // re-verifying the unmarshallerFactory ordering issue does not return.
             if (log.isDebugEnabled()) {
                 log.debug("WSS4J initialization starting");
             }
@@ -391,12 +403,22 @@ public class RampartMessageData {
                 if (policyDataRampartConfig != null) {
                     String timeToLiveString = policyDataRampartConfig.getTimestampTTL();
                     if (timeToLiveString != null && !timeToLiveString.equals("")) {
-                        this.setTimeToLive(Integer.parseInt(timeToLiveString));
+                        try {
+                            this.setTimeToLive(Integer.parseInt(timeToLiveString));
+                        } catch (NumberFormatException e) {
+                            throw new RampartException("invalidRampartConfigValue",
+                                    new String[]{"timestampTTL", timeToLiveString}, e);
+                        }
                     }
 
                     String maxSkewString = policyDataRampartConfig.getTimestampMaxSkew();
                     if (maxSkewString != null && !maxSkewString.equals("")) {
-                        this.setTimestampMaxSkew(Integer.parseInt(maxSkewString));
+                        try {
+                            this.setTimestampMaxSkew(Integer.parseInt(maxSkewString));
+                        } catch (NumberFormatException e) {
+                            throw new RampartException("invalidRampartConfigValue",
+                                    new String[]{"timestampMaxSkew", maxSkewString}, e);
+                        }
                     }
                 }
                 
diff --git a/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties b/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
index cf5ed2b4..b4df6ed2 100644
--- a/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
+++ b/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
@@ -16,6 +16,7 @@
 
 
 missingConfiguration = Missing or malformed configuration: \"{0}\"
+invalidRampartConfigValue = Invalid value \"{1}\" for Rampart configuration parameter \"{0}\"
 expectedParameterMissing = Expected parameter missing : \"{0}\" 
 missingScopeValue = Missing or incorrect scope value
 canotFindContextIdentifier = Cannot find context identifier


(axis-axis2-java-rampart) 01/06: RAMPART-427: add the CLIENT_SIDE marker parameter race-free

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit 8ce0aeb037fd5a45e93dd815e5e13263e77e5be7
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Tue Jun 9 16:38:22 2026 -1000

    RAMPART-427: add the CLIENT_SIDE marker parameter race-free
    
    RampartMessageData's constructor set the per-service CLIENT_SIDE marker parameter
    with a non-atomic check-then-add: concurrent first requests to a service all saw
    the parameter as absent, then each tried to add it with setLocked(true). The first
    won; the rest failed with "The CLIENT_SIDE parameter is already locked and the
    value cannot be overridden", failing those requests intermittently under load.
    
    The add is now done by addClientSideParameterIfAbsent(AxisService), which
    synchronizes on the service and re-checks before adding, so the parameter is added
    at most once. The lock is per-service (not a global static), and the common path -
    parameter already present - never enters the synchronized method, so steady-state
    message processing stays lock-free.
    
    Adds RampartMessageDataTest covering idempotent and concurrent adds. (The original
    report's integration test relied on commons-httpclient 3.x, which is no longer a
    dependency, and was an inherently timing-dependent test; this deterministic unit
    test replaces it.) Verified with a full clean -Papache-release verify across all
    modules including the integration scenarios and the nine policy samples on JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../org/apache/rampart/RampartMessageData.java     | 26 +++++--
 .../org/apache/rampart/RampartMessageDataTest.java | 83 ++++++++++++++++++++++
 2 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java b/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
index e2a122f9..8ba45a90 100644
--- a/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
+++ b/modules/rampart-core/src/main/java/org/apache/rampart/RampartMessageData.java
@@ -209,6 +209,27 @@ public class RampartMessageData {
     
     private SOAPConstants soapConstants;
 
+    /**
+     * Adds the locked CLIENT_SIDE marker parameter to the service, once, if it is
+     * not already present. The check-and-add is synchronized on the service so that
+     * concurrent first requests do not race to add it. Without this, the first
+     * thread added and locked the parameter and the remaining threads failed with
+     * "The CLIENT_SIDE parameter is already locked and the value cannot be
+     * overridden" (RAMPART-427). The lock is per-service rather than global, and the
+     * common case (parameter already present) never reaches this method, so there is
+     * no contention once a service has handled its first client request.
+     */
+    static void addClientSideParameterIfAbsent(AxisService axisService) throws AxisFault {
+        synchronized (axisService) {
+            if (axisService.getParameter(PARAM_CLIENT_SIDE) == null) {
+                Parameter clientSideParam = new Parameter();
+                clientSideParam.setName(PARAM_CLIENT_SIDE);
+                clientSideParam.setLocked(true);
+                axisService.addParameter(clientSideParam);
+            }
+        }
+    }
+
     public RampartMessageData(MessageContext msgCtx, boolean sender) throws RampartException {
         
         this.msgContext = msgCtx;
@@ -267,10 +288,7 @@ public class RampartMessageData {
                 this.isInitiator = !msgCtx.isServerSide();
                 //TODO if Axis Service is null at this point, do we have to create a dummy one ??    
                 if(this.isInitiator && axisService != null ) {
-                    Parameter clientSideParam = new Parameter();
-                    clientSideParam.setName(PARAM_CLIENT_SIDE);
-                    clientSideParam.setLocked(true);
-                    msgCtx.getAxisService().addParameter(clientSideParam);
+                    addClientSideParameterIfAbsent(axisService);
                 }
             }
 
diff --git a/modules/rampart-core/src/test/java/org/apache/rampart/RampartMessageDataTest.java b/modules/rampart-core/src/test/java/org/apache/rampart/RampartMessageDataTest.java
new file mode 100644
index 00000000..fcb493c4
--- /dev/null
+++ b/modules/rampart-core/src/test/java/org/apache/rampart/RampartMessageDataTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.rampart;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+import org.apache.axis2.description.AxisService;
+
+/**
+ * RAMPART-427: adding the locked CLIENT_SIDE marker parameter to a service must be
+ * idempotent and safe under concurrent first requests; otherwise all but the first
+ * thread fails with "The CLIENT_SIDE parameter is already locked and the value
+ * cannot be overridden".
+ */
+public class RampartMessageDataTest extends TestCase {
+
+    /**
+     * Deterministic guard: a second add must not throw because the parameter is
+     * already present and locked.
+     */
+    public void testAddClientSideParameterIsIdempotent() throws Exception {
+        AxisService service = new AxisService("TestService");
+        RampartMessageData.addClientSideParameterIfAbsent(service);
+        RampartMessageData.addClientSideParameterIfAbsent(service);
+        assertNotNull("CLIENT_SIDE parameter should be present",
+                service.getParameter(RampartMessageData.PARAM_CLIENT_SIDE));
+    }
+
+    /**
+     * Many threads racing to mark the same service client-side must all succeed,
+     * with the parameter added exactly once.
+     */
+    public void testAddClientSideParameterConcurrent() throws Exception {
+        final AxisService service = new AxisService("TestService");
+        final int threadCount = 16;
+        final CountDownLatch startGate = new CountDownLatch(1);
+        final CountDownLatch doneGate = new CountDownLatch(threadCount);
+        final List<Throwable> failures = new CopyOnWriteArrayList<Throwable>();
+
+        for (int i = 0; i < threadCount; i++) {
+            new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        startGate.await();
+                        RampartMessageData.addClientSideParameterIfAbsent(service);
+                    } catch (Throwable t) {
+                        failures.add(t);
+                    } finally {
+                        doneGate.countDown();
+                    }
+                }
+            }).start();
+        }
+
+        startGate.countDown(); // release all threads at once to maximise contention
+        doneGate.await();
+
+        assertTrue("concurrent CLIENT_SIDE add must not fail (RAMPART-427): " + failures,
+                failures.isEmpty());
+        assertNotNull("CLIENT_SIDE parameter should be present",
+                service.getParameter(RampartMessageData.PARAM_CLIENT_SIDE));
+    }
+}


(axis-axis2-java-rampart) 05/06: RAMPART-44 / RAMPART-252: enforce the policy algorithm suite on inbound messages

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit 07701083cf6984de90d83a3a6c826ad71c7b2f70
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jun 10 07:46:05 2026 -1000

    RAMPART-44 / RAMPART-252: enforce the policy algorithm suite on inbound messages
    
    Rampart did not check that the algorithms actually used in an incoming message
    matched the policy's algorithm suite, so a peer could downgrade to weaker
    signature / digest / canonicalization / encryption / key-wrap algorithms and the
    message would still be accepted (RAMPART-44).
    
    RampartEngine now builds a WSS4J AlgorithmSuite from the policy's AlgorithmSuite
    and sets it on the RequestData (applyAlgorithmSuite), so WSS4J rejects a message
    whose algorithms are not those the policy mandates. Only the policy-defined
    categories are constrained; unspecified categories and the key-length bounds keep
    WSS4J's defaults, so legitimate messages are unaffected (verified across the
    Basic128/Basic256/TripleDes-RSA15, SAML and SecureConversation integration
    scenarios and the nine policy samples).
    
    This implements the algorithm-validation half of RAMPART-252, which supersedes
    RAMPART-44. The other half of RAMPART-252 (SAML issued-token referencelist
    processing) depended on WSS4J's WSS-206, which was never applied upstream, and is
    out of scope here.
    
    RampartTest case 34 (asymmetric client against a symmetric service) is now
    rejected by algorithm-suite enforcement - on the key-wrap algorithm - during
    security-header processing rather than by the older certificate-presence
    heuristic. The negative test is relaxed to assert that the request is rejected
    rather than matching the previous specific message. Note: WSS4J's
    AlgorithmSuiteValidator raises a terse "INVALID_SECURITY" fault for a suite
    violation; surfacing a clearer message is a possible follow-up.
    
    Verified with a full clean -Papache-release verify on JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../java/org/apache/rampart/RampartEngine.java     | 50 ++++++++++++++++++++++
 .../test/java/org/apache/rampart/RampartTest.java  | 10 ++++-
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java b/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java
index 540fdf1a..c8897f17 100644
--- a/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java
+++ b/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java
@@ -234,6 +234,11 @@ public class RampartEngine {
             requestData.setEncodePasswords(false); // default
 	}
 
+        // RAMPART-44 / RAMPART-252: constrain the algorithms accepted on the incoming
+        // message to those permitted by the policy's algorithm suite, so a peer cannot
+        // downgrade to weaker algorithms than the policy requires.
+        applyAlgorithmSuite(requestData, rpd);
+
         ValidatorData data = new ValidatorData(rmd);
 
 		SOAPHeader header = rmd.getMsgContext().getEnvelope().getHeader();
@@ -418,6 +423,51 @@ public class RampartEngine {
 		return results;
 	}
 
+    /**
+     * Builds a WSS4J algorithm suite from the policy's algorithm suite and sets it on
+     * the request data, so WSS4J rejects an incoming message whose signature, digest,
+     * canonicalization, encryption or key-wrap algorithm is not the one the policy
+     * mandates (RAMPART-44 / RAMPART-252). Categories that are not populated are left
+     * unrestricted by WSS4J; the symmetric/asymmetric key-length bounds keep WSS4J's
+     * sensible defaults. SAML assertion algorithms are validated separately and are
+     * intentionally left untouched here.
+     */
+    private void applyAlgorithmSuite(RequestData requestData, RampartPolicyData rpd) {
+        org.apache.ws.secpolicy.model.AlgorithmSuite policySuite = rpd.getAlgorithmSuite();
+        if (policySuite == null) {
+            return;
+        }
+
+        org.apache.wss4j.common.crypto.AlgorithmSuite wss4jSuite =
+                new org.apache.wss4j.common.crypto.AlgorithmSuite();
+
+        addAlgorithm(policySuite.getAsymmetricSignature(), wss4jSuite, AlgorithmKind.SIGNATURE);
+        addAlgorithm(policySuite.getSymmetricSignature(), wss4jSuite, AlgorithmKind.SIGNATURE);
+        addAlgorithm(policySuite.getDigest(), wss4jSuite, AlgorithmKind.DIGEST);
+        addAlgorithm(policySuite.getInclusiveC14n(), wss4jSuite, AlgorithmKind.C14N);
+        addAlgorithm(policySuite.getEncryption(), wss4jSuite, AlgorithmKind.ENCRYPTION);
+        addAlgorithm(policySuite.getSymmetricKeyWrap(), wss4jSuite, AlgorithmKind.KEY_WRAP);
+        addAlgorithm(policySuite.getAsymmetricKeyWrap(), wss4jSuite, AlgorithmKind.KEY_WRAP);
+
+        requestData.setAlgorithmSuite(wss4jSuite);
+    }
+
+    private enum AlgorithmKind { SIGNATURE, DIGEST, C14N, ENCRYPTION, KEY_WRAP }
+
+    private void addAlgorithm(String algorithm, org.apache.wss4j.common.crypto.AlgorithmSuite suite,
+            AlgorithmKind kind) {
+        if (algorithm == null || algorithm.length() == 0) {
+            return;
+        }
+        switch (kind) {
+            case SIGNATURE:  suite.addSignatureMethod(algorithm); break;
+            case DIGEST:     suite.addDigestAlgorithm(algorithm); break;
+            case C14N:       suite.addC14nAlgorithm(algorithm); break;
+            case ENCRYPTION: suite.addEncryptionMethod(algorithm); break;
+            case KEY_WRAP:   suite.addKeyWrapAlgorithm(algorithm); break;
+        }
+    }
+
     private WSHandlerResult processSecurityHeaderWithRSA15(RampartMessageData rmd, SOAPHeaderBlock secHeader, WSSecurityEngine engine,
     		Crypto signatureCrypto, Crypto decCrypto, TokenCallbackHandler tokenCallbackHandler,
 		RequestData requestData) 
diff --git a/modules/rampart-integration/src/test/java/org/apache/rampart/RampartTest.java b/modules/rampart-integration/src/test/java/org/apache/rampart/RampartTest.java
index 246d40f6..4f6c103f 100644
--- a/modules/rampart-integration/src/test/java/org/apache/rampart/RampartTest.java
+++ b/modules/rampart-integration/src/test/java/org/apache/rampart/RampartTest.java
@@ -18,6 +18,7 @@ package org.apache.rampart;
 
 import static org.apache.axis2.integration.TestConstants.TESTING_PATH;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
 import org.apache.axiom.om.OMAbstractFactory;
@@ -145,7 +146,14 @@ public class RampartTest {
                         if (i == 28) {
                             assertEquals(resources.getString("encryptionMissing"), axisFault.getMessage());
                         } else if (i == 34) {
-                            assertEquals(resources.getString("invalidSignatureAlgo"), axisFault.getMessage());
+                            // RAMPART-44 / RAMPART-252: an asymmetric-binding message sent to a
+                            // symmetric-binding service uses algorithms (here the key-wrap algorithm)
+                            // that the service's algorithm suite does not permit. It is now rejected by
+                            // algorithm-suite enforcement during security-header processing rather than
+                            // by the older certificate-presence heuristic, so assert that the request was
+                            // rejected rather than matching a specific message.
+                            assertNotNull("Case 34 (binding/algorithm mismatch) must be rejected",
+                                    axisFault.getMessage());
                         }
 
                     }


(axis-axis2-java-rampart) 04/06: RAMPART-431: skip signing SignedParts headers that are absent from the message

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit 205ae0c19bd5b0889cf91504a89cca2c950c9428
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jun 10 05:53:43 2026 -1000

    RAMPART-431: skip signing SignedParts headers that are absent from the message
    
    TransportBindingBuilder built signature parts directly from the policy's
    SignedParts headers without checking whether each header is actually present in
    the message. When a header listed in SignedParts is missing - e.g. the
    WS-Addressing wsa:To header in an STS bootstrap / SecureConversation request -
    WSS4J fails the whole send with "General security error ... Element to
    encrypt/sign not found: http://www.w3.org/2005/08/addressing, To".
    
    The four signature methods (X509 / Kerberos / IssuedToken / SecureConversation)
    now add a SignedParts header only if a matching element exists in the SOAP header,
    via the new addSignedPartHeaderIfPresent helper. This mirrors RampartUtil's
    outbound part resolution and the inbound validator, both of which treat an absent
    signed-parts header as allowed.
    
    Adds a regression test (TransportBindingBuilderTest.testTransportBindingAbsentSignedHeader
    with rampart-transport-binding-absent-signed-header.xml): a policy whose endorsing
    token signs a 2005/08 wsa:To header not present in the message now builds
    successfully. With the presence check disabled the test reproduces the original
    "Element to encrypt/sign not found" error. Verified with a full clean
    -Papache-release verify across all modules including the nine policy samples on
    JDK 25.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../rampart/builder/TransportBindingBuilder.java   | 49 ++++++++++-----
 .../rampart/TransportBindingBuilderTest.java       | 23 +++++++
 ...part-transport-binding-absent-signed-header.xml | 70 ++++++++++++++++++++++
 3 files changed, 126 insertions(+), 16 deletions(-)

diff --git a/modules/rampart-core/src/main/java/org/apache/rampart/builder/TransportBindingBuilder.java b/modules/rampart-core/src/main/java/org/apache/rampart/builder/TransportBindingBuilder.java
index 5e37596f..cc065a47 100644
--- a/modules/rampart-core/src/main/java/org/apache/rampart/builder/TransportBindingBuilder.java
+++ b/modules/rampart-core/src/main/java/org/apache/rampart/builder/TransportBindingBuilder.java
@@ -18,6 +18,7 @@ package org.apache.rampart.builder;
 
 import org.apache.axiom.om.OMElement;
 import org.apache.axiom.soap.SOAPEnvelope;
+import org.apache.axiom.soap.SOAPHeader;
 import org.apache.axis2.context.MessageContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -55,6 +56,7 @@ import javax.crypto.KeyGenerator;
 import javax.xml.crypto.dsig.Reference;
 import javax.xml.crypto.dsig.SignatureMethod;
 
+import javax.xml.namespace.QName;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.Iterator;
@@ -185,6 +187,33 @@ public class TransportBindingBuilder extends BindingBuilder {
      * @param token
      * @param signdParts 
      */
+    /**
+     * Adds a SignedParts header to the list of signature parts, but only if a matching
+     * header is actually present in the message. Signing a part that is not present
+     * makes WSS4J fail with "Element to encrypt/sign not found" - for example when a
+     * policy lists wsa:To in SignedParts but the WS-Addressing headers are absent, as
+     * happens for an STS bootstrap request (RAMPART-431). This mirrors the inbound
+     * validator, which treats an absent signed-parts header as allowed.
+     */
+    private void addSignedPartHeaderIfPresent(List<WSEncryptionPart> sigParts,
+            SOAPEnvelope envelope, Header header) {
+        SOAPHeader soapHeader = envelope.getHeader();
+        if (soapHeader == null) {
+            return;
+        }
+        boolean present;
+        if (header.getName() == null) {
+            // The policy matches all headers in the namespace.
+            present = !soapHeader.getHeaderBlocksWithNSURI(header.getNamespace()).isEmpty();
+        } else {
+            present = soapHeader.getFirstChildWithName(
+                    new QName(header.getNamespace(), header.getName())) != null;
+        }
+        if (present) {
+            sigParts.add(new WSEncryptionPart(header.getName(), header.getNamespace(), "Content"));
+        }
+    }
+
     private byte[] doX509TokenSignature(RampartMessageData rmd, Token token, SignedEncryptedParts signdParts) throws RampartException {
         
         RampartPolicyData rpd = rmd.getPolicyData();
@@ -205,10 +234,7 @@ public class TransportBindingBuilder extends BindingBuilder {
             ArrayList headers = signdParts.getHeaders();
             for (Iterator iterator = headers.iterator(); iterator.hasNext();) {
                 Header header = (Header) iterator.next();
-                WSEncryptionPart wep = new WSEncryptionPart(header.getName(), 
-                        header.getNamespace(),
-                        "Content");
-                sigParts.add(wep);
+                addSignedPartHeaderIfPresent(sigParts, rmd.getMsgContext().getEnvelope(), header);
             }
         }
         if(token.isDerivedKeys()) {
@@ -345,10 +371,7 @@ public class TransportBindingBuilder extends BindingBuilder {
             ArrayList headers = signdParts.getHeaders();
             for (Iterator iterator = headers.iterator(); iterator.hasNext();) {
                 Header header = (Header) iterator.next();
-                WSEncryptionPart wep = new WSEncryptionPart(header.getName(), 
-                        header.getNamespace(),
-                        "Content");
-                sigParts.add(wep);
+                addSignedPartHeaderIfPresent(sigParts, rmd.getMsgContext().getEnvelope(), header);
             }
         }
 
@@ -457,10 +480,7 @@ public class TransportBindingBuilder extends BindingBuilder {
             ArrayList headers = signdParts.getHeaders();
             for (Object signedHeader : headers) {
                 Header header = (Header) signedHeader;
-                WSEncryptionPart wep = new WSEncryptionPart(header.getName(),
-                        header.getNamespace(),
-                        "Content");
-                sigParts.add(wep);
+                addSignedPartHeaderIfPresent(sigParts, rmd.getMsgContext().getEnvelope(), header);
             }
         }
         
@@ -610,10 +630,7 @@ public class TransportBindingBuilder extends BindingBuilder {
             ArrayList headers = signdParts.getHeaders();
             for (Object objectHeader : headers) {
                 Header header = (Header) objectHeader;
-                WSEncryptionPart wep = new WSEncryptionPart(header.getName(),
-                        header.getNamespace(),
-                        "Content");
-                sigParts.add(wep);
+                addSignedPartHeaderIfPresent(sigParts, rmd.getMsgContext().getEnvelope(), header);
             }
         }
         
diff --git a/modules/rampart-tests/src/test/java/org/apache/rampart/TransportBindingBuilderTest.java b/modules/rampart-tests/src/test/java/org/apache/rampart/TransportBindingBuilderTest.java
index a39e3619..99019e6b 100644
--- a/modules/rampart-tests/src/test/java/org/apache/rampart/TransportBindingBuilderTest.java
+++ b/modules/rampart-tests/src/test/java/org/apache/rampart/TransportBindingBuilderTest.java
@@ -46,6 +46,29 @@ public class TransportBindingBuilderTest extends MessageBuilderTestBase {
         this.verifySecHeader(list.iterator(), ctx.getEnvelope());
     }
 
+    public void testTransportBindingAbsentSignedHeader() throws Exception {
+        // RAMPART-431: the policy lists a WS-Addressing 2005/08 "To" header in the
+        // endorsing token's SignedParts, but the message does not contain that header.
+        // The build must succeed (the absent header is skipped) rather than fail with
+        // "Element to encrypt/sign not found: http://www.w3.org/2005/08/addressing, To".
+        MessageContext ctx = getMsgCtx();
+
+        String policyXml = "test-resources/policy/rampart-transport-binding-absent-signed-header.xml";
+        Policy policy = this.loadPolicy(policyXml);
+
+        ctx.setProperty(RampartMessageData.KEY_RAMPART_POLICY, policy);
+
+        MessageBuilder builder = new MessageBuilder();
+        builder.build(ctx);
+
+        List<QName> list = new ArrayList<QName>();
+        list.add(new QName(WSConstants.WSU_NS, WSConstants.TIMESTAMP_TOKEN_LN));
+        list.add(new QName(WSConstants.WSSE_NS, WSConstants.USERNAME_TOKEN_LN));
+        list.add(new QName(WSConstants.WSSE_NS, WSConstants.BINARY_TOKEN_LN));
+        list.add(new QName(WSConstants.SIG_NS, WSConstants.SIG_LN));
+        this.verifySecHeader(list.iterator(), ctx.getEnvelope());
+    }
+
     public void testTransportBindingNoBST() throws Exception {
         MessageContext ctx = getMsgCtx();
 
diff --git a/modules/rampart-tests/test-resources/policy/rampart-transport-binding-absent-signed-header.xml b/modules/rampart-tests/test-resources/policy/rampart-transport-binding-absent-signed-header.xml
new file mode 100644
index 00000000..6a66a7d5
--- /dev/null
+++ b/modules/rampart-tests/test-resources/policy/rampart-transport-binding-absent-signed-header.xml
@@ -0,0 +1,70 @@
+<wsp:Policy wsu:Id="5" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
+<wsp:ExactlyOne>
+  <wsp:All>
+	<sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+	  <wsp:Policy>
+		<sp:TransportToken>
+		  <wsp:Policy>
+			<sp:HttpsToken RequireClientCertificate="false"/>
+		  </wsp:Policy>
+		</sp:TransportToken>
+		<sp:AlgorithmSuite>
+		  <wsp:Policy>
+			<sp:Basic128/>
+		  </wsp:Policy>
+		</sp:AlgorithmSuite>
+		<sp:Layout>
+		  <wsp:Policy>
+			<sp:Lax/>
+		  </wsp:Policy>
+		</sp:Layout>
+		<sp:IncludeTimestamp/>
+	  </wsp:Policy>
+	</sp:TransportBinding>
+	<sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+		<wsp:Policy>
+			<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient" />
+	  </wsp:Policy>
+	</sp:SignedSupportingTokens>
+	<sp:SignedEndorsingSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+	  <wsp:Policy>
+		<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient" />
+		<!-- RAMPART-431: this WS-Addressing 2005/08 "To" header is NOT present in the
+		     test message (which carries a 2004/08 "To"). Signing must skip it rather
+		     than fail with "Element to encrypt/sign not found". -->
+		<sp:SignedParts>
+			<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
+		</sp:SignedParts>
+	  </wsp:Policy>
+	</sp:SignedEndorsingSupportingTokens>
+	<sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+	  <wsp:Policy>
+		<sp:MustSupportRefKeyIdentifier/>
+		<sp:MustSupportRefIssuerSerial/>
+	  </wsp:Policy>
+	</sp:Wss10>
+	<sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
+	  <wsp:Policy>
+		<sp:MustSupportIssuedTokens/>
+		<sp:RequireClientEntropy/>
+		<sp:RequireServerEntropy/>
+	  </wsp:Policy>
+	</sp:Trust10>
+
+	<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
+		<ramp:user>alice</ramp:user>
+		<ramp:encryptionUser>bob</ramp:encryptionUser>
+		<ramp:passwordCallbackClass>org.apache.rampart.TestCBHandler</ramp:passwordCallbackClass>
+
+		<ramp:signatureCrypto>
+			<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
+				<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">pkcs12</ramp:property>
+				<ramp:property name="org.apache.ws.security.crypto.merlin.file">test-resources/keys/interop2024.pkcs12</ramp:property>
+				<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
+			</ramp:crypto>
+		</ramp:signatureCrypto>
+	</ramp:RampartConfig>
+
+  </wsp:All>
+</wsp:ExactlyOne>
+</wsp:Policy>