This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/trust-nokeyinfo in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 5a472a670ff76aed7ac1ffdb1deec420cae955b1 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Sep 18 11:51:37 2026 +0100 Set trust when there is no KeyInfo and the certificate comes from a local keystore --- .../wss4j/dom/processor/SignatureProcessor.java | 7 ++++ .../apache/wss4j/dom/message/SignatureTest.java | 42 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java index 9a2a43778..8db6097e7 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java @@ -145,6 +145,13 @@ public class SignatureProcessor implements Processor { if (keyInfoElement == null) { certs = getDefaultCerts(data.getSigVerCrypto()); principal = certs[0].getSubjectX500Principal(); + // The message carries no KeyInfo, so the signing certificate was not supplied by the + // sender: it was taken from the receiver's own signature verification keystore, under + // its configured default alias. The credential is therefore trusted by construction + // and there is nothing here for a Validator to decide - an attacker cannot influence + // which certificate the signature is verified against. Verifying the signature against + // it does establish the sender's identity, so the result may be stamped as validated. + trustEstablished = true; } else { int result = 0; Node node = keyInfoElement.getFirstChild(); diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureTest.java index 3b276ea0e..074a19dee 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureTest.java @@ -127,6 +127,48 @@ public class SignatureTest { "An X.509 signature that passed trust validation must be reported as validated"); } + /** + * A Signature with no KeyInfo at all. The signing certificate is then not supplied by the + * sender, but taken from the receiver's own signature verification keystore under its + * configured default alias, so an attacker cannot influence which certificate the signature + * is verified against. That credential is trusted by construction and the result must be + * reported as validated - otherwise a consumer keying off TAG_VALIDATED_TOKEN treats a + * signature by the operator's own configured identity as untrusted. + */ + @Test + public void testX509SignatureNoKeyInfo() throws Exception { + Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); + WSSecHeader secHeader = new WSSecHeader(doc); + secHeader.insertSecurityHeader(); + + WSSecSignature builder = new WSSecSignature(secHeader); + builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security"); + Document signedDoc = builder.build(crypto); + + // Strip the KeyInfo. It is not referenced by the SignedInfo, so the signature still + // verifies, and the receiver falls back to the default certificate of its own keystore - + // which crypto.properties configures to the certificate used above. + Element signature = + XMLUtils.findElement(signedDoc.getDocumentElement(), "Signature", WSConstants.SIG_NS); + assertNotNull(signature); + Element keyInfo = XMLUtils.getDirectChildElement(signature, "KeyInfo", WSConstants.SIG_NS); + assertNotNull(keyInfo); + signature.removeChild(keyInfo); + + if (LOG.isDebugEnabled()) { + LOG.debug(XMLUtils.prettyDocumentToString(signedDoc)); + } + + WSHandlerResult results = verify(signedDoc); + + WSSecurityEngineResult actionResult = + results.getActionResults().get(WSConstants.SIGN).get(0); + assertNotNull(actionResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE)); + assertTrue((Boolean)actionResult.get(WSSecurityEngineResult.TAG_VALIDATED_TOKEN), + "A signature verified against the receiver's own default certificate must be " + + "reported as validated"); + } + @Test public void testX509SignatureISAttached() throws Exception { Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
