From 5f5b1a57d7d45406c84d62b7a28dc95575e7250c Mon Sep 17 00:00:00 2001 From: wandi34 Date: Wed, 7 Aug 2024 14:17:54 +0200 Subject: [PATCH 1/3] SWS-1058 - Enable sign with cert chain and configuration of subjectDnConstraints (#1419) Co-authored-by: Andreas Winter --- .../wss4j2/Wss4jSecurityInterceptor.java | 34 ++++++++++++++++++ .../Wss4jMessageInterceptorSignTestCase.java | 34 ++++++++++++++++++ .../src/test/resources/private.jks | Bin 1807 -> 7217 bytes 3 files changed, 68 insertions(+) diff --git a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java index 3b457cf46..c04cfc58a 100644 --- a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java +++ b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java @@ -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; @@ -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 @@ -138,6 +144,7 @@ * @author Jamin Hitchcock * @author Rob Leland * @author Lars Uffmann + * @author Andreas Winter * @see Apache WSS4J 2.0 * @since 2.3.0 */ @@ -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 signatureSubjectDnPatterns = emptyList(); + /** * Create a {@link WSSecurityEngine} by default. */ @@ -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); } @@ -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 WSS4J configuration: SIG_SUBJECT_CERT_CONSTRAINTS + */ + public void setValidationSubjectDnConstraints(List patterns) { + signatureSubjectDnPatterns = patterns; + } + /** Whether to enable signatureConfirmation or not. By default signatureConfirmation is enabled */ public void setEnableSignatureConfirmation(boolean enableSignatureConfirmation) { @@ -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; } @@ -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; } diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSignTestCase.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSignTestCase.java index 0e8490af9..729221b73 100644 --- a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSignTestCase.java +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSignTestCase.java @@ -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; @@ -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"); + } } diff --git a/spring-ws-security/src/test/resources/private.jks b/spring-ws-security/src/test/resources/private.jks index b3b10e36f8eb4b106279725d60034bdd9ebc4388..15a4ebafe8b2da377c7b2ec6a658bad8b7bf484e 100644 GIT binary patch delta 5494 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 Date: Wed, 7 Aug 2024 08:44:47 -0500 Subject: [PATCH 2/3] Migrate build to Develocity Conventions Maven extension. (#1426) --- .gitignore | 1 - .mvn/develocity.xml | 30 ------------------------- .mvn/extensions.xml | 11 +++------ Jenkinsfile | 29 ++++++++---------------- ci/build-and-deploy-to-artifactory.sh | 3 --- ci/build-and-deploy-to-maven-central.sh | 3 --- ci/pipeline.properties | 3 +-- ci/test.sh | 3 --- settings.xml | 5 ----- 9 files changed, 13 insertions(+), 75 deletions(-) delete mode 100644 .mvn/develocity.xml diff --git a/.gitignore b/.gitignore index fc4ed6e86..365f5b205 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,4 @@ _site credentials.yml .flattened-pom.xml -.mvn/.gradle-enterprise .mvn/.develocity diff --git a/.mvn/develocity.xml b/.mvn/develocity.xml deleted file mode 100644 index b54815e75..000000000 --- a/.mvn/develocity.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - https://ge.spring.io - - - false - true - true - - #{{'0.0.0.0'}} - - - - - true - - - - - ${env.GRADLE_ENTERPRISE_CACHE_USERNAME} - ${env.GRADLE_ENTERPRISE_CACHE_PASSWORD} - - - true - #{env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null and env['GRADLE_ENTERPRISE_CACHE_PASSWORD'] != null} - - - \ No newline at end of file diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 854714cee..a36ba0531 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -1,13 +1,8 @@ - com.gradle - develocity-maven-extension - 1.21.3 - - - com.gradle - common-custom-user-data-maven-extension - 2.0 + io.spring.develocity.conventions + develocity-conventions-maven-extension + 0.0.19 \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index f479045e6..bc34f8b63 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,8 +37,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -63,8 +62,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -82,8 +80,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -101,8 +98,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -120,8 +116,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -137,8 +132,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { @@ -161,8 +155,7 @@ pipeline { KEYRING = credentials('spring-signing-secring.gpg') PASSPHRASE = credentials('spring-gpg-passphrase') STAGING_PROFILE_ID = credentials('spring-data-release-deployment-maven-central-staging-profile-id') - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -234,8 +227,7 @@ pipeline { KEYRING = credentials('spring-signing-secring.gpg') PASSPHRASE = credentials('spring-gpg-passphrase') STAGING_PROFILE_ID = credentials('spring-data-release-deployment-maven-central-staging-profile-id') - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -294,8 +286,7 @@ pipeline { environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -303,8 +294,6 @@ pipeline { docker.withRegistry('', "${p['dockerhub.credentials']}") { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ' + - 'GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} ' + - 'GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} ' + './mvnw -s settings.xml -Pjakarta-ee-10,distribute,docs ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + diff --git a/ci/build-and-deploy-to-artifactory.sh b/ci/build-and-deploy-to-artifactory.sh index 5960f8b46..e8e5eb91b 100755 --- a/ci/build-and-deploy-to-artifactory.sh +++ b/ci/build-and-deploy-to-artifactory.sh @@ -4,9 +4,6 @@ set -euo pipefail RELEASE_TYPE=$1 -export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} -export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} - echo 'Deploying to Artifactory...' MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" ./mvnw \ diff --git a/ci/build-and-deploy-to-maven-central.sh b/ci/build-and-deploy-to-maven-central.sh index 9eb2a5730..5a48d3762 100755 --- a/ci/build-and-deploy-to-maven-central.sh +++ b/ci/build-and-deploy-to-maven-central.sh @@ -5,9 +5,6 @@ set -euo pipefail PROJECT_VERSION=$1 STAGING_REPOSITORY_ID=$2 -export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} -export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} - echo 'Staging on Maven Central...' GNUPGHOME=/tmp/gpghome diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 2aea08744..09915555f 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -18,5 +18,4 @@ docker.java.inside.basic=-v $HOME:/tmp/jenkins-home # Credentials artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c dockerhub.credentials=hub.docker.com-springbuildmaster -gradle-enterprise-cache.credentials=gradle_enterprise_cache_user -gradle-enterprise.access-key=gradle_enterprise_secret_access_key +develocity.access-key=gradle_enterprise_secret_access_key diff --git a/ci/test.sh b/ci/test.sh index ba458fba6..5ec6309d8 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -2,9 +2,6 @@ set -euo pipefail -export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} -export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} - MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml \ -P${PROFILE} clean dependency:list test -Dsort -B -U diff --git a/settings.xml b/settings.xml index 28f0d1339..b474fd4ff 100644 --- a/settings.xml +++ b/settings.xml @@ -24,11 +24,6 @@ ${env.ARTIFACTORY_USR} ${env.ARTIFACTORY_PSW} - - ge.spring.io - ${env.GRADLE_ENTERPRISE_CACHE_USERNAME} - ${env.GRADLE_ENTERPRISE_CACHE_PASSWORD} - \ No newline at end of file From 0ad78d14b733d768440bd186fd3481c020e934f4 Mon Sep 17 00:00:00 2001 From: LDTO Thomas Date: Tue, 27 Aug 2024 13:28:11 +0700 Subject: [PATCH 3/3] handle null as best practice --- .../soap/client/SoapFaultClientException.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/client/SoapFaultClientException.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/client/SoapFaultClientException.java index 0ba93c4c5..f2e2e7d8b 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/soap/client/SoapFaultClientException.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/client/SoapFaultClientException.java @@ -23,6 +23,8 @@ import org.springframework.ws.soap.SoapFault; import org.springframework.ws.soap.SoapMessage; +import java.util.Optional; + /** * Thrown by {@code SoapFaultMessageResolver} when the response message has a fault. * @@ -56,13 +58,17 @@ public QName getFaultCode() { } /** - * Returns the fault string or reason. For SOAP 1.1, this returns the fault string. For SOAP 1.2, this returns the - * fault reason for the default locale. + * Returns the fault string or reason from the SOAP fault. + * For SOAP 1.1, this method returns the fault string. For SOAP 1.2, it returns the fault reason for the default locale. + *

+ * Note that this method returns the same value as {@link #getMessage()}. *

- * Note that this message returns the same as {@link #getMessage()}. + * The returned value is wrapped in an {@link Optional}. If the SOAP fault is not present, an empty {@link Optional} is returned. + * + * @return an {@link Optional} containing the fault string or reason if present, or an empty {@link Optional} if the SOAP fault is not present. */ - public String getFaultStringOrReason() { - return soapFault != null ? soapFault.getFaultStringOrReason() : null; + public Optional getFaultStringOrReason() { + return Optional.ofNullable(this.soapFault) + .map(SoapFault::getFaultStringOrReason); } - }