Search code examples
apachesslapache-camelcxf

Apache Camel CXF: add TlsClientParameters programmatically


I am using Apache Camel CXF as producer to call a SOAP Webservice. I do not use Spring configuration but do everything programmatically (I am a beginner and wanted to prevent having to learn both Spring and Apache Camel). The Webservice uses SSL with a self signed certificate. I added it to a truststore and hoped to be able to add that to the CxfEndpoint similar to how I did it with https4:

KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("src/main/resources/truststore.jks");
ksp.setPassword("...");

KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("...");

SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);

CamelContext context = new DefaultCamelContext();
context.addRoutes(routeBuilder);

HttpComponent httpComponent = context.getComponent("https4", HttpComponent.class);
httpComponent.setSslContextParameters(scp);

– but that does not seem to work with the CxfComponent. I found a lot of documentation about adding TlsClientParameters using Spring and configuring the CxfEndpoint, for example here: apache camel cxf https not working and here Calling secure webservice using CXF and Camel. However I do not find any hint on how to simply add a truststore to the component as I did with https4 or even in the route definition, which is:

from(ENDPOINT_URI)
.setProperty(SecurityConstants.PASSWORD, constant(PASSWORD))
.setProperty(SecurityConstants.USERNAME, constant(USERNAME))
.to("cxf://" + SERVICE_URL + "?" +
     "wsdlURL=" + WSDL_URL + "&" +
      "serviceName=" + SERVICE_NAME + "&" +
      "portName=" + PORT_NAME + "&" +
      "dataFormat=CXF_MESSAGE&" +
      "synchronous=true&" +
      "defaultOperationName=" + DEFAULT_OPERATION_NAME)
.streamCaching();

I think this must be a very simple problem, so I still expect there is some neat way to simply add the truststore (or even accepting any certificate, since its not really relevant in our use case). I would be really happy if there was a simple programmatic way. Does anyone know?


Solution

  • I solved the issue by adding the certificate to the JVMs truststore in jre/lib/cacerts. That is feasable since I have access to the JVM on the machine the application will be running on. It seems to be the simplest solution.

    Update

    If anyone is interested in a more proper solution: CxfEndpoint provides a means to influence the HTTPConduit and its TLS Parameters. This is the revised code:

    • add "cxfEndpointConfigurer=SageEndpointConfigurer" to the cxf endpoint parameters
    • when creating the endpoint "SageEndpointConfigurer" will be resolved using TypeConverters
    • add a TypeConverter to the TypeConverter Registry of the context, i.e. directly in the RouteBuilder getContext().getTypeConverterRegistry().addTypeConverter(CxfEndpointConfigurer.class, String.class, new SageEndpointConfigurerConverter());
    • configure TLSParameters and simply return the CxfEndpointConfigurer from the TypeConverter

      private class SageEndpointConfigurerConverter extends TypeConverterSupport {
      
      @Override
      public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {
          CxfEndpointConfigurer configurer = new CxfEndpointConfigurer() {
              @Override
              public void configure(AbstractWSDLBasedEndpointFactory factoryBean) {
                  // do nothing
              }
      
              @Override
              public void configureClient(Client client) {
                  URLConnectionHTTPConduit conduit = (URLConnectionHTTPConduit) client.getConduit();
                  TLSClientParameters tlsParams = new TLSClientParameters();
                  tlsParams.setDisableCNCheck(true);
                  tlsParams.setTrustManagers(new TrustManager[]{new TrustAllTrustManager()});
                  conduit.setTlsClientParameters(tlsParams);
              }
      
              @Override
              public void configureServer(Server server) {
                  //do nothing
              }
          };
          return (T) configurer;
      }
      }
      
    • the TrustAllManager is implemented like that

      public class TrustAllTrustManager implements X509TrustManager {
      
      private static Logger LOG = LoggerFactory.getLogger(TrustAllTrustManager.class);
      
      @Override
      public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
      //do nothing, trust all certificates
      logMessage(x509Certificates, authType);
      }
      
      @Override
      public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
      //do nothing, trust all certificates
      logMessage(x509Certificates, authType);
      }
      
      @Override
      public X509Certificate[] getAcceptedIssuers() {
      return new X509Certificate[0];
      }
      
      private void logMessage(X509Certificate[] x509Certificates, String authType) {
      StringBuilder message = new StringBuilder();
      String lineSeparator = System.getProperty("line.separator");
      message.append("Trusted following certificates for authentication type '").append(authType).append("'").append(lineSeparator);
      for (X509Certificate certificate : x509Certificates) {
          message.append(certificate).append(lineSeparator);
      }
      LOG.trace(message.toString());
      }
      }