4

I am developing Spring Boot Application v2.0. From my Spring Boot App I am sending SOAP Request to get data from Soap WS. I have multiple Soap Request to different Soap Web Services. Every Soap WS has it's own certificate. I used Apache CXF v3.2.4 to auto generate "ws client" classes and everything else for WS.

Certificates are in PFX format. I have successfully create keystore via keytool. I tried to set up ssl.keyStore with this code ( also this is not a good way to set up these values, I assume it's better to do it in application-properties...:

System.setProperty("javax.net.ssl.keyStore","path to my keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "mypassword");

I have 3 different certs in my keystore. All of them are tested separately and all of them works fine if they are only one in keystore. Problem is that when I have for example 3 certs in keystore, only first on the list is loaded.

I read multiple articles on Internet, this article was most interesting but unfortunately it didn't solve my problem (Registering multiple keystores in JVM) .

If I go through certs chain by alias I can see all certificates in the Console.

 String storename = "C:/Certificates/mykeystore.ks";
            char[] storepass = "mypassword".toCharArray();
            String alias = "myalias";
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(storename), storepass);             
            java.security.cert.Certificate[] cchain = ks.getCertificateChain(alias);            
            List mylist = new ArrayList();
        for (int i = 0; i < cchain.length; i++) {                 
            mylist.add(cchain[i]);
            }
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            CertPath cp = cf.generateCertPath(mylist);
            System.out.println(cp);

Do you have any suggestion?! What should I do in order to achieve stage that I can either load one keystore with multiple certificates or anything that will work? Thanks in advance.

p.s. Also I tried to put these certs in jdk/jre/lib/security/cacerts via Portecle but no effect.

I.R.1989
  • 77
  • 1
  • 6

1 Answers1

2

For each Web Service, I think you should handle the keystores (and/or truststores) separately and to be configured in the application.properties file. Below is a workable example of instantiating a SocketFactory for the Url Connection with the specific sets of certificate(s).

application.properties

keystore1.Path = src/main/resources/jks/yourKeyStoreFile1
keystore1.Password = keystore1pwd
truststore1.Path = src/main/resources/security1/cacerts
truststore1.Password = truststore1pwd

keystore2.Path = src/main/resources/jks/yourKeyStoreFile2
keystore2.Password = keystore2pwd
truststore2.Path = src/main/resources/security2/cacerts
truststore2.Password = truststore2pwd

When you are consuming different WebService, use a different SocketFactory. Below is a sample of .p12 keystore plus a .jks truststore (cert). You could easily convert the type of certificate into different format.

@Component
public class MySocketFactory {

    @Value("${keystore1.Path}")
    String keystore1Path;

    @Value("${keystore1.Password}")
    String keystore1Password;

    @Value("${truststore1.Path}")
    String truststore1Path;

    @Value("${truststore1.Password}")
    String truststore1Password;

    public MySocketFactory() {

    }

    public SSLSocketFactory getSocketFactory() {

        try {

            SSLContext context = SSLContext.getInstance("TLS");

            KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance("SunX509");
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            char[] keyStorePassword = keystore1Password.toCharArray();
            keyStore.load(new FileInputStream(keystore1Path), keyStorePassword);
            keyMgrFactory.init(keyStore, keyStorePassword);

            TrustManagerFactory trustStrFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            KeyStore trustStore = KeyStore.getInstance("JKS");
            char[] trustStorePassword = truststore1Password.toCharArray();
            trustStore.load(new FileInputStream(truststore1Path), trustStorePassword);
            trustStrFactory.init(trustStore);

            context.init(keyMgrFactory.getKeyManagers(), trustStrFactory.getTrustManagers(), null);
            return context.getSocketFactory();

        } catch (Exception e) {
            System.err.println("Failed to create a server socket factory...");
            e.printStackTrace();
            return null;
        }
    }
}

Create the same for the second SocketFactory. Hope this helps.

lazyduckiy
  • 281
  • 2
  • 9
  • this helped me a lot in my test app which is Dynamic Web Project, but still doesn't work with Spring Boot 2.0.3 . I think it's maybe something with Spring Boot and embedded Tomcat. I cannot initialize Keystore at all... I always get `[nio-8080-exec-1] org.apache.cxf.transport.https.SSLUtils : Default key managers cannot be initialized: null` – I.R.1989 Jul 04 '18 at 15:31
  • I tried also with BindingProvider `BindingProvider bindingProvider = (BindingProvider) port; bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getSocketFactory(truststorePath,trustStorePass));` – I.R.1989 Jul 04 '18 at 15:31
  • Did you @Autowired the SocketFactory into the Service Client class where you need to use the SocketFactory? You just need to autowire, and no instantiation required. – lazyduckiy Jul 09 '18 at 03:23