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 <[email protected]> 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) <[email protected]> --- .../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>
