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 <[email protected]>
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>

Reply via email to