You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ff...@apache.org on 2026/02/12 15:05:51 UTC
(camel) 01/02: [CAMEL-22977]DefaultCxfBinding: also populate credentials
This is an automated email from the ASF dual-hosted git repository.
ffang pushed a commit to branch camel-4.14.x
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9fee6a1b8c34dfc5587952c12158c41d03b65505
Author: Freeman Fang <fr...@gmail.com>
AuthorDate: Thu Feb 12 07:57:22 2026 -0500
[CAMEL-22977]DefaultCxfBinding: also populate credentials
(cherry picked from commit 4afa70f3c83d4911d67989cfd6725f0e60107384)
---
components/camel-cxf/camel-cxf-soap/pom.xml | 1 -
.../component/cxf/jaxws/DefaultCxfBinding.java | 106 ++++++++++++++++++++-
.../camel/AssertSubjectHasX509CertsProcessor.java | 51 ++++++++++
.../cxf/wssecurity/camel/camel-context.xml | 2 +
4 files changed, 158 insertions(+), 2 deletions(-)
diff --git a/components/camel-cxf/camel-cxf-soap/pom.xml b/components/camel-cxf/camel-cxf-soap/pom.xml
index 7614e0aa43e7..9f043a8ce6fd 100644
--- a/components/camel-cxf/camel-cxf-soap/pom.xml
+++ b/components/camel-cxf/camel-cxf-soap/pom.xml
@@ -194,7 +194,6 @@
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>${cxf-version}</version>
- <scope>test</scope>
</dependency>
diff --git a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java
index 326cd86d1e6c..2ba23b415b4d 100644
--- a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java
+++ b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/DefaultCxfBinding.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -94,6 +95,9 @@ import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.MessagePartInfo;
import org.apache.cxf.service.model.OperationInfo;
import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
+import org.apache.wss4j.dom.handler.WSHandlerConstants;
+import org.apache.wss4j.dom.handler.WSHandlerResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -368,18 +372,118 @@ public class DefaultCxfBinding implements CxfBinding, HeaderFilterStrategyAware
SecurityContext securityContext = cxfMessage.get(SecurityContext.class);
if (securityContext instanceof LoginSecurityContext
&& ((LoginSecurityContext) securityContext).getSubject() != null) {
+ Subject subject = ((LoginSecurityContext) securityContext).getSubject();
+ // attach certs to the subject instance
+ addInboundX509CertificatesToSubject(cxfMessage, subject);
camelExchange.getIn().getHeaders().put(CxfConstants.AUTHENTICATION,
- ((LoginSecurityContext) securityContext).getSubject());
+ subject);
} else if (securityContext != null) {
Principal user = securityContext.getUserPrincipal();
if (user != null) {
Subject subject = new Subject();
subject.getPrincipals().add(user);
+ // attach certs to the subject instance
+ addInboundX509CertificatesToSubject(cxfMessage, subject);
camelExchange.getIn().getHeaders().put(CxfConstants.AUTHENTICATION, subject);
}
}
}
+ private static void addInboundX509CertificatesToSubject(Message cxfMessage, Subject subject) {
+ if (cxfMessage == null || subject == null) {
+ return;
+ }
+ // If it’s read-only, don’t break the route; just skip.
+ if (subject.isReadOnly()) {
+ return;
+ }
+
+ final Object recv = cxfMessage.get(WSHandlerConstants.RECV_RESULTS);
+ if (recv == null) {
+ return;
+ }
+
+ // We only need the cert objects.
+ Collection<X509Certificate> certs = null;
+
+ if (recv instanceof Map) {
+ Object v = ((Map<?, ?>) recv).get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
+ if (v instanceof Collection) {
+ certs = new ArrayList<>();
+ for (Object o : (Collection<?>) v) {
+ if (o instanceof X509Certificate) {
+ certs.add((X509Certificate) o);
+ } else if (o instanceof X509Certificate[]) {
+ for (X509Certificate c : (X509Certificate[]) o) {
+ if (c != null) {
+ certs.add(c);
+ }
+ }
+ }
+ }
+ } else if (v instanceof X509Certificate[]) {
+ certs = new ArrayList<>();
+ for (X509Certificate c : (X509Certificate[]) v) {
+ if (c != null) {
+ certs.add(c);
+ }
+ }
+ }
+ } else if (recv instanceof List) {
+ // Typical CXF case: List<WSHandlerResult>
+ List<?> list = (List<?>) recv;
+ if (!list.isEmpty() && list.get(0) instanceof WSHandlerResult) {
+ certs = new ArrayList<>();
+ for (Object hrObj : list) {
+ WSHandlerResult hr = (WSHandlerResult) hrObj;
+ // WSHandlerResult contains List<WSSecurityEngineResult>
+ for (WSSecurityEngineResult r : hr.getResults()) {
+ Object v = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
+ if (v instanceof X509Certificate[]) {
+ for (X509Certificate c : (X509Certificate[]) v) {
+ if (c != null) {
+ certs.add(c);
+ }
+ }
+ } else if (v instanceof X509Certificate) {
+ certs.add((X509Certificate) v);
+ } else {
+ Object leaf = r.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
+ if (leaf instanceof X509Certificate) {
+ certs.add((X509Certificate) leaf);
+ }
+ }
+ }
+ }
+ if (certs.isEmpty()) {
+ certs = null;
+ }
+ }
+ }
+ if (certs == null || certs.isEmpty()) {
+ return;
+ }
+
+ Set<Object> pub = (Set<Object>) subject.getPublicCredentials();
+ for (Object o : certs) {
+ if (o instanceof X509Certificate) {
+ pub.add(o);
+ } else if (o instanceof X509Certificate[]) {
+ for (X509Certificate c : (X509Certificate[]) o) {
+ if (c != null) {
+ pub.add(c);
+ }
+ }
+ } else if (o instanceof Collection) {
+ for (Object nested : (Collection<?>) o) {
+ if (nested instanceof X509Certificate) {
+ pub.add(nested);
+ }
+ }
+ }
+ }
+ }
+
private static void setOperationNameViaMethod(Exchange camelExchange, Method method) {
camelExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, method.getName());
if (LOG.isTraceEnabled()) {
diff --git a/components/camel-cxf/camel-cxf-spring-soap/src/test/java/org/apache/camel/component/cxf/wssecurity/camel/AssertSubjectHasX509CertsProcessor.java b/components/camel-cxf/camel-cxf-spring-soap/src/test/java/org/apache/camel/component/cxf/wssecurity/camel/AssertSubjectHasX509CertsProcessor.java
new file mode 100644
index 000000000000..b7beb44af925
--- /dev/null
+++ b/components/camel-cxf/camel-cxf-spring-soap/src/test/java/org/apache/camel/component/cxf/wssecurity/camel/AssertSubjectHasX509CertsProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.cxf.wssecurity.camel;
+
+import java.security.cert.X509Certificate;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.component.cxf.common.message.CxfConstants;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AssertSubjectHasX509CertsProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) {
+ Object auth = exchange.getIn().getHeader(CxfConstants.AUTHENTICATION);
+ assertNotNull(auth, "Expected " + CxfConstants.AUTHENTICATION + " header");
+ assertTrue(auth instanceof Subject, "Expected AUTHENTICATION to be a Subject but was: "
+ + auth.getClass());
+
+ Subject subject = (Subject) auth;
+ Set<X509Certificate> certs = subject.getPublicCredentials(X509Certificate.class);
+
+ assertNotNull(certs, "Subject public credentials should not be null");
+ assertFalse(certs.isEmpty(), "Expected at least one X509Certificate in Subject public credentials");
+
+ for (X509Certificate c : certs) {
+ assertNotNull(c);
+ assertNotNull(c.getSubjectX500Principal());
+ }
+ }
+}
diff --git a/components/camel-cxf/camel-cxf-spring-soap/src/test/resources/org/apache/camel/component/cxf/wssecurity/camel/camel-context.xml b/components/camel-cxf/camel-cxf-spring-soap/src/test/resources/org/apache/camel/component/cxf/wssecurity/camel/camel-context.xml
index 3e10b4fe291c..089a72d4410e 100644
--- a/components/camel-cxf/camel-cxf-spring-soap/src/test/resources/org/apache/camel/component/cxf/wssecurity/camel/camel-context.xml
+++ b/components/camel-cxf/camel-cxf-spring-soap/src/test/resources/org/apache/camel/component/cxf/wssecurity/camel/camel-context.xml
@@ -111,6 +111,7 @@
<camelContext xmlns="http://camel.apache.org/schema/spring" id="camel">
<route errorHandlerRef="noErrorHandler">
<from uri="cxf:bean:signatureRoute"/>
+ <process ref="assertSubjectHasX509Certs"/>
<to uri="cxf:bean:signatureService"/>
</route>
<route errorHandlerRef="noErrorHandler">
@@ -128,6 +129,7 @@
</camelContext>
<bean id="noErrorHandler" class="org.apache.camel.builder.NoErrorHandlerBuilder"/>
+ <bean id="assertSubjectHasX509Certs" class="org.apache.camel.component.cxf.wssecurity.camel.AssertSubjectHasX509CertsProcessor"/>
<bean id="wss4jInInterceptor-signature" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>