Skip to content

Commit

Permalink
SWS-1058 - Enable sign with cert chain and configuration of subjectDn…
Browse files Browse the repository at this point in the history
…Constraints (#1419)

Co-authored-by: Andreas Winter <[email protected]>
  • Loading branch information
wandi34 and Andreas Winter authored Aug 7, 2024
1 parent 87ec638 commit 5f5b1a5
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
Expand Down Expand Up @@ -59,6 +62,9 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;

/**
* A WS-Security endpoint interceptor based on Apache's WSS4J. This interceptor supports messages created by the
* {@link org.springframework.ws.soap.axiom.AxiomSoapMessageFactory} and the
Expand Down Expand Up @@ -138,6 +144,7 @@
* @author Jamin Hitchcock
* @author Rob Leland
* @author Lars Uffmann
* @author Andreas Winter
* @see <a href="http://ws.apache.org/wss4j/">Apache WSS4J 2.0</a>
* @since 2.3.0
*/
Expand Down Expand Up @@ -194,6 +201,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 = emptyList();

/**
* Create a {@link WSSecurityEngine} by default.
*/
Expand Down Expand Up @@ -225,6 +234,15 @@ public void setSecurementActor(String securementActor) {
handler.setOption(WSHandlerConstants.ACTOR, securementActor);
}

/**
* Defines whether to use a single certificate or a whole certificate chain when constructing
* a BinarySecurityToken used for direct reference in signature.
* The default is "true", meaning that only a single certificate is used.
*/
public void setSecurementSignatureSingleCertificate(boolean useSingleCertificate) {
handler.setOption(WSHandlerConstants.USE_SINGLE_CERTIFICATE, useSingleCertificate);
}

public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
}
Expand Down Expand Up @@ -485,6 +503,19 @@ public void setValidationSignatureCrypto(Crypto signatureCrypto) {
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 <a href="https://ws.apache.org/wss4j/config.html">WSS4J configuration: SIG_SUBJECT_CERT_CONSTRAINTS</a>
*/
public void setValidationSubjectDnConstraints(List<Pattern> patterns) {
signatureSubjectDnPatterns = patterns;
}

/** Whether to enable signatureConfirmation or not. By default signatureConfirmation is enabled */
public void setEnableSignatureConfirmation(boolean enableSignatureConfirmation) {

Expand Down Expand Up @@ -670,6 +701,7 @@ protected RequestData initializeRequestData(MessageContext messageContext) {
// allow for qualified password types for .Net interoperability
requestData.setAllowNamespaceQualifiedPasswordTypes(true);

requestData.setSubjectCertConstraints(signatureSubjectDnPatterns);
return requestData;
}

Expand Down Expand Up @@ -710,6 +742,8 @@ protected RequestData initializeValidationRequestData(MessageContext messageCont
// allow for qualified password types for .Net interoperability
requestData.setAllowNamespaceQualifiedPasswordTypes(true);

requestData.setSubjectCertConstraints(signatureSubjectDnPatterns);

return requestData;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

import static org.assertj.core.api.Assertions.*;

import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;

import org.junit.jupiter.api.Test;
import org.springframework.ws.WebServiceMessage;
Expand Down Expand Up @@ -121,4 +123,36 @@ public void testSignResponseWithSignatureUser() throws Exception {
assertXpathExists("Absent SignatureConfirmation element",
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature", document);
}

@Test
public void testValidateCertificateSubjectDnConstraintsShouldMatchSubject() throws Exception {
SoapMessage message = createSignedTestSoapMessage();
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
interceptor.secureMessage(message, messageContext);

interceptor.setValidationActions("Signature");
interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile(".*")));
assertThatCode(() ->interceptor.validateMessage(message, messageContext)).doesNotThrowAnyException();
}

@Test
public void testValidateCertificateSubjectDnConstraintsShouldFailForNotMatchingSubject() throws Exception {
SoapMessage message = createSignedTestSoapMessage();
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
interceptor.secureMessage(message, messageContext);

interceptor.setValidationActions("Signature");
interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile("O=Some Other Company")));
Throwable catched = catchThrowable(() -> interceptor.validateMessage(message, messageContext));
assertThat(catched).isInstanceOf(Wss4jSecurityValidationException.class);
}

private SoapMessage createSignedTestSoapMessage() throws Exception {
interceptor.setSecurementActions("Signature");
interceptor.setSecurementSignatureKeyIdentifier("DirectReference");
interceptor.setSecurementSignatureSingleCertificate(false);
interceptor.setSecurementPassword("123456");
interceptor.setSecurementUsername("testkey");
return loadSoap11Message("empty-soap.xml");
}
}
Binary file modified spring-ws-security/src/test/resources/private.jks
Binary file not shown.

0 comments on commit 5f5b1a5

Please sign in to comment.