Merge pull request #1419 from wandi34

* pr/1419:
  Polish "Support WSS4J subject cert constraints"
  Support WSS4J subject cert constraints

Closes gh-1419
This commit is contained in:
Stéphane Nicoll
2025-03-11 11:31:58 +01:00
3 changed files with 53 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -208,6 +209,8 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
// To maintain same behavior as default, this flag is set to true
private boolean removeSecurityHeader = true;
private List<Pattern> signatureSubjectDnPatterns = Collections.emptyList();
/**
* Create a {@link WSSecurityEngine} by default.
*/
@@ -527,6 +530,17 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
this.validationSignatureCrypto = signatureCrypto;
}
/**
* Certificate constraints which will be applied to the subject DN of the certificate
* used for signature validation, after trust verification of the certificate chain
* associated with the certificate.
* @param patterns a list of regex patterns which will be applied to the subject DN.
* @see ConfigurationConstants#SIG_SUBJECT_CERT_CONSTRAINTS
*/
public void setValidationSubjectDnConstraints(List<Pattern> patterns) {
this.signatureSubjectDnPatterns = patterns;
}
/**
* Whether to enable signatureConfirmation or not. By default, signatureConfirmation
* is enabled.
@@ -741,6 +755,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
// allow for qualified password types for .Net interoperability
requestData.setAllowNamespaceQualifiedPasswordTypes(true);
requestData.setSubjectCertConstraints(this.signatureSubjectDnPatterns);
return requestData;
}
@@ -780,6 +795,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
// allow for qualified password types for .Net interoperability
requestData.setAllowNamespaceQualifiedPasswordTypes(true);
requestData.setSubjectCertConstraints(this.signatureSubjectDnPatterns);
return requestData;
}

View File

@@ -16,7 +16,9 @@
package org.springframework.ws.soap.security.wss4j2;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
@@ -28,6 +30,8 @@ import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
public abstract class Wss4jMessageInterceptorSignTest extends Wss4jTest {
@@ -123,4 +127,37 @@ public abstract class Wss4jMessageInterceptorSignTest extends Wss4jTest {
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature", document);
}
@Test
public void testValidateCertificateSubjectDnConstraintsShouldMatchSubject() throws Exception {
SoapMessage message = createSignedTestSoapMessage();
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
this.interceptor.secureMessage(message, messageContext);
this.interceptor.setValidationActions("Signature");
this.interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile(".*")));
assertThatCode(() -> this.interceptor.validateMessage(message, messageContext)).doesNotThrowAnyException();
}
@Test
public void testValidateCertificateSubjectDnConstraintsShouldFailForNotMatchingSubject() throws Exception {
SoapMessage message = createSignedTestSoapMessage();
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
this.interceptor.secureMessage(message, messageContext);
this.interceptor.setValidationActions("Signature");
this.interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile("O=Some Other Company")));
assertThatExceptionOfType(Wss4jSecurityValidationException.class)
.isThrownBy(() -> this.interceptor.validateMessage(message, messageContext))
.withMessage("The security token could not be authenticated or authorized");
}
private SoapMessage createSignedTestSoapMessage() throws Exception {
this.interceptor.setSecurementActions("Signature");
this.interceptor.setSecurementSignatureKeyIdentifier("DirectReference");
this.interceptor.setUseSingleCertificate(false);
this.interceptor.setSecurementPassword("123456");
this.interceptor.setSecurementUsername("testkey");
return loadSoap11Message("empty-soap.xml");
}
}