Skip to content

Commit 8460ad8

Browse files
Andreas Wintersnicoll
Andreas Winter
authored andcommitted
Support WSS4J subject cert constraints
See gh-1419
1 parent 4b13ab3 commit 8460ad8

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java

+30
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.ArrayList;
2323
import java.util.Collections;
2424
import java.util.List;
25+
import java.util.regex.Pattern;
2526

2627
import javax.security.auth.callback.Callback;
2728
import javax.security.auth.callback.CallbackHandler;
@@ -60,6 +61,8 @@
6061
import org.springframework.ws.soap.security.callback.CleanupCallback;
6162
import org.springframework.ws.soap.security.wss4j2.callback.UsernameTokenPrincipalCallback;
6263

64+
import static java.util.Collections.emptyList;
65+
6366
/**
6467
* A WS-Security endpoint interceptor based on Apache's WSS4J. This interceptor supports
6568
* messages created by the
@@ -208,6 +211,8 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
208211
// To maintain same behavior as default, this flag is set to true
209212
private boolean removeSecurityHeader = true;
210213

214+
private List<Pattern> signatureSubjectDnPatterns = emptyList();
215+
211216
/**
212217
* Create a {@link WSSecurityEngine} by default.
213218
*/
@@ -239,6 +244,15 @@ public void setSecurementActor(String securementActor) {
239244
this.handler.setOption(WSHandlerConstants.ACTOR, securementActor);
240245
}
241246

247+
/**
248+
* Defines whether to use a single certificate or a whole certificate chain when
249+
* constructing a BinarySecurityToken used for direct reference in signature. The
250+
* default is "true", meaning that only a single certificate is used.
251+
*/
252+
public void setSecurementSignatureSingleCertificate(boolean useSingleCertificate) {
253+
handler.setOption(WSHandlerConstants.USE_SINGLE_CERTIFICATE, useSingleCertificate);
254+
}
255+
242256
public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
243257
this.handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
244258
}
@@ -527,6 +541,19 @@ public void setValidationSignatureCrypto(Crypto signatureCrypto) {
527541
this.validationSignatureCrypto = signatureCrypto;
528542
}
529543

544+
/**
545+
* Certificate constraints which will be applied to the subject DN of the certificate
546+
* used for signature validation, after trust verification of the certificate chain
547+
* associated with the certificate.
548+
* @param patterns A list of regex patterns which will be applied to the subject DN.
549+
*
550+
* @see <a href="https://ws.apache.org/wss4j/config.html">WSS4J configuration:
551+
* SIG_SUBJECT_CERT_CONSTRAINTS</a>
552+
*/
553+
public void setValidationSubjectDnConstraints(List<Pattern> patterns) {
554+
signatureSubjectDnPatterns = patterns;
555+
}
556+
530557
/**
531558
* Whether to enable signatureConfirmation or not. By default, signatureConfirmation
532559
* is enabled.
@@ -741,6 +768,7 @@ protected RequestData initializeRequestData(MessageContext messageContext) {
741768
// allow for qualified password types for .Net interoperability
742769
requestData.setAllowNamespaceQualifiedPasswordTypes(true);
743770

771+
requestData.setSubjectCertConstraints(signatureSubjectDnPatterns);
744772
return requestData;
745773
}
746774

@@ -780,6 +808,8 @@ protected RequestData initializeValidationRequestData(MessageContext messageCont
780808
// allow for qualified password types for .Net interoperability
781809
requestData.setAllowNamespaceQualifiedPasswordTypes(true);
782810

811+
requestData.setSubjectCertConstraints(signatureSubjectDnPatterns);
812+
783813
return requestData;
784814
}
785815

spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSignTest.java

+36
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.ws.soap.security.wss4j2;
1818

19+
import java.util.List;
1920
import java.util.Properties;
21+
import java.util.regex.Pattern;
2022

2123
import org.junit.jupiter.api.Test;
2224
import org.w3c.dom.Document;
@@ -28,6 +30,8 @@
2830
import org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean;
2931

3032
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatCode;
34+
import static org.assertj.core.api.Assertions.catchThrowable;
3135

3236
public abstract class Wss4jMessageInterceptorSignTest extends Wss4jTest {
3337

@@ -123,4 +127,36 @@ public void testSignResponseWithSignatureUser() throws Exception {
123127
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature", document);
124128
}
125129

130+
@Test
131+
public void testValidateCertificateSubjectDnConstraintsShouldMatchSubject() throws Exception {
132+
SoapMessage message = createSignedTestSoapMessage();
133+
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
134+
interceptor.secureMessage(message, messageContext);
135+
136+
interceptor.setValidationActions("Signature");
137+
interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile(".*")));
138+
assertThatCode(() -> interceptor.validateMessage(message, messageContext)).doesNotThrowAnyException();
139+
}
140+
141+
@Test
142+
public void testValidateCertificateSubjectDnConstraintsShouldFailForNotMatchingSubject() throws Exception {
143+
SoapMessage message = createSignedTestSoapMessage();
144+
MessageContext messageContext = getSoap11MessageContext(createSignedTestSoapMessage());
145+
interceptor.secureMessage(message, messageContext);
146+
147+
interceptor.setValidationActions("Signature");
148+
interceptor.setValidationSubjectDnConstraints(List.of(Pattern.compile("O=Some Other Company")));
149+
Throwable catched = catchThrowable(() -> interceptor.validateMessage(message, messageContext));
150+
assertThat(catched).isInstanceOf(Wss4jSecurityValidationException.class);
151+
}
152+
153+
private SoapMessage createSignedTestSoapMessage() throws Exception {
154+
interceptor.setSecurementActions("Signature");
155+
interceptor.setSecurementSignatureKeyIdentifier("DirectReference");
156+
interceptor.setSecurementSignatureSingleCertificate(false);
157+
interceptor.setSecurementPassword("123456");
158+
interceptor.setSecurementUsername("testkey");
159+
return loadSoap11Message("empty-soap.xml");
160+
}
161+
126162
}
Binary file not shown.

0 commit comments

Comments
 (0)