Search code examples
springdockersslcxfkeytool

Apache CXF JaxWsProxyFactoryBean SSL Configuration call to external https fails


I have a spring microservice docker deployed. I use JaxWsProxyFactoryBean to call an external server (soap/wsdl) and all goes well using http://externalServer:

JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("http://externalServer");
...

The problem raise on when I call https in setAddress.

I registered the key/certificate, of the called server, in keystore using keytool and saving to /root/.keystore (the standard) importing it from pfx. When I try to call the https I obtain this error:

org.apache.cxf.transport.https.SSLUtils  : Default key managers cannot be initialized: Password must not be null

Ok, I'm missing a password. But where put this maleficent password? In the application.yml? In system property? [The password used in keystore is the standard (changeit)]

EDITED

Here a snapshot of the log:

DEBUG 1 --- [  XNIO-1 task-1] org.apache.cxf.transport.https.SSLUtils  : The location of the key store has not been set via a system parameter or through configuration so the default value of /root/.keystore will be used.
DEBUG 1 --- [  XNIO-1 task-1] org.apache.cxf.transport.https.SSLUtils  : The key store password has not been set via a system property or through configuration, reading data from the keystore will fail.
DEBUG 1 --- [  XNIO-1 task-1] org.apache.cxf.transport.https.SSLUtils  : The key password has not been set via a system property or through configuration, reading data from the keystore will fail.
DEBUG 1 --- [  XNIO-1 task-1] org.apache.cxf.transport.https.SSLUtils  : The keystore type has not been set in configuration so the default value of JKS will be used.
WARN 1 --- [  XNIO-1 task-1] org.apache.cxf.transport.https.SSLUtils  : Default key managers cannot be initialized: Password must not be null

Standard keystore is used but with no password.


Solution

  • Looking at our conversation under the comment section from your question I can conclude that your Apache CXF does not have ssl configured. What you need to do is read your keystore containing the trusted certificates and load it into your SSLContext with the TrustManagerFactory and TrustManager.

    Below is an example configuration which you can try out:

    Option 1 - Plain Java

    import org.apache.cxf.bus.CXFBusFactory;
    import org.apache.cxf.configuration.jsse.TLSClientParameters;
    import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
    import org.apache.cxf.jaxrs.client.WebClient;
    import org.apache.cxf.transport.http.HTTPConduitConfigurer;
    
    import javax.net.ssl.KeyManager;
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.TrustManagerFactory;
    import java.nio.file.Paths;
    import java.security.KeyStore;
    
    public class App {
    
        public static void main(String[] args) throws Exception {
            InputStream identityAsInputStream = Files.newInputStream(Paths.get("/path/to/your/identity.jks"));
            KeyStore identity = KeyStore.getInstance(KeyStore.getDefaultType());
            identity.load(identityAsInputStream, "keystore-password".toCharArray());
    
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(identity, "key-password".toCharArray());
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
    
            InputStream trustStoreInputStream = Files.newInputStream(Paths.get("/path/to/your/truststore.jks"));
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(trustStoreInputStream, "truststore-password".toCharArray());
    
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagers, trustManagers, null);
    
            JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
            factory.setAddress("https://some-secure-server.com/");
    
            factory.setBus(new CXFBusFactory().createBus());
            factory.getBus().setExtension((name, address, httpConduit) -> {
                TLSClientParameters tls = new TLSClientParameters();
                tls.setSSLSocketFactory(sslContext.getSocketFactory());
                httpConduit.setTlsClientParameters(tls);
            }, HTTPConduitConfigurer.class);
    
            WebClient webClient = factory.createWebClient();
        }
    }
    

    Option 2 - Simplified configuration with library

    import nl.altindag.ssl.SSLFactory;
    import org.apache.cxf.bus.CXFBusFactory;
    import org.apache.cxf.configuration.jsse.TLSClientParameters;
    import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
    import org.apache.cxf.jaxrs.client.WebClient;
    import org.apache.cxf.transport.http.HTTPConduitConfigurer;
    
    import java.nio.file.Paths;
    
    public class App {
    
        public static void main(String[] args) throws Exception {
            SSLFactory sslFactory = SSLFactory.builder()
                    .withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "keystore-password".toCharArray())
                    .withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "truststore-password".toCharArray())
                    .build();
    
            JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
            factory.setAddress("https://some-secure-server.com/");
    
            factory.setBus(new CXFBusFactory().createBus());
            factory.getBus().setExtension((name, address, httpConduit) -> {
                TLSClientParameters tls = new TLSClientParameters();
                tls.setSSLSocketFactory(sslFactory.getSslSocketFactory());
                httpConduit.setTlsClientParameters(tls);
            }, HTTPConduitConfigurer.class);
    
            WebClient webClient = factory.createWebClient();
        }
    }
    

    The library is available here: GitHub - SSLContext Kickstart

    I also have here a working example of the cxf client for one-way and two-way authentication based on ssl: GitHub - Example Apache CXF client ssl configuration