This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/encrypted-header in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit fd7215dc27ed4d321602babd1e4acef418718c4f Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Sep 11 11:09:32 2026 +0100 EncryptedHeader child confusion causing wrong protected-header selection --- .../org/apache/wss4j/dom/util/EncryptionUtils.java | 23 ++++++++++- .../apache/wss4j/dom/message/EncryptionTest.java | 48 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/EncryptionUtils.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/EncryptionUtils.java index f01f07a1b..9240f725f 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/EncryptionUtils.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/EncryptionUtils.java @@ -90,6 +90,12 @@ public final class EncryptionUtils { while (child != null && child.getNodeType() != Node.ELEMENT_NODE) { child = child.getNextSibling(); } + if (child == null || !"EncryptedData".equals(child.getLocalName()) + || !WSConstants.ENC_NS.equals(child.getNamespaceURI())) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.INVALID_SECURITY, "dataRef", + new Object[] {dataRefURI}); + } return (Element)child; } return encryptedDataElement; @@ -218,7 +224,22 @@ public final class EncryptionUtils { || parent.getLocalName().equals(WSConstants.ENCRYPED_ASSERTION_LN) && parent.getNamespaceURI().equals(WSConstants.SAML2_NS)) { - Node decryptedHeader = parent.getFirstChild(); + // The decrypted node sits where the EncryptedData was, which is not necessarily + // the wrapper's first child - anything else there was never encrypted. + Node decryptedHeader = decryptedNode; + if (decryptedHeader == null) { + if (content) { + decryptedHeader = encData; + } else if (previousSibling == null) { + decryptedHeader = parent.getFirstChild(); + } else { + decryptedHeader = previousSibling.getNextSibling(); + } + } + if (decryptedHeader == null || Node.ELEMENT_NODE != decryptedHeader.getNodeType()) { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK); + } + Node soapHeader = parent.getParentNode(); soapHeader.replaceChild(decryptedHeader, parent); diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java index 3d51055bf..7078cf6a5 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java @@ -62,6 +62,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -92,6 +93,53 @@ public class EncryptionTest { crypto = CryptoFactory.getInstance("wss40.properties"); } + @Test + public void testEncryptedHeaderWithUnencryptedFirstChild() throws Exception { + Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); + WSSecHeader secHeader = new WSSecHeader(doc); + secHeader.insertSecurityHeader(); + Element soapHeader = (Element)doc.getElementsByTagNameNS( + WSConstants.URI_SOAP11_ENV, "Header").item(0); + Element header = doc.createElementNS("urn:test", "test:header"); + header.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:test", "urn:test"); + soapHeader.appendChild(header); + + WSSecEncrypt builder = new WSSecEncrypt(secHeader); + builder.setUserInfo("wss40"); + builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); + builder.getParts().add(new WSEncryptionPart("header", "urn:test", "Header")); + + Document encryptedDoc = builder.build(crypto, key); + Element encryptedHeader = (Element)encryptedDoc.getElementsByTagNameNS( + WSConstants.WSSE11_NS, WSConstants.ENCRYPTED_HEADER).item(0); + Node encryptedData = encryptedHeader.getFirstChild(); + while (encryptedData != null && encryptedData.getNodeType() != Node.ELEMENT_NODE) { + encryptedData = encryptedData.getNextSibling(); + } + Element unencryptedHeader = encryptedDoc.createElementNS( + "urn:attacker", "attacker:header"); + unencryptedHeader.setAttributeNS( + WSConstants.XMLNS_NS, "xmlns:attacker", "urn:attacker"); + encryptedHeader.insertBefore(unencryptedHeader, encryptedData); + + WSHandlerResult results = secEngine.processSecurityHeader( + encryptedDoc, null, keystoreCallbackHandler, crypto); + + Element promotedHeader = (Element)encryptedDoc.getElementsByTagNameNS( + "urn:test", "header").item(0); + assertNotNull(promotedHeader); + assertEquals("urn:test", promotedHeader.getNamespaceURI()); + assertEquals("header", promotedHeader.getLocalName()); + assertEquals(0, encryptedDoc.getElementsByTagNameNS( + "urn:attacker", "header").getLength()); + + @SuppressWarnings("unchecked") + java.util.List<WSDataRef> dataRefs = (java.util.List<WSDataRef>)results.getActionResults() + .get(WSConstants.ENCR).get(0).get(WSSecurityEngineResult.TAG_DATA_REF_URIS); + assertEquals(1, dataRefs.size()); + assertEquals(promotedHeader, dataRefs.get(0).getProtectedElement()); + } + /** * Setup method *
