Thanks to the combination of Kannan's and Niraj's answer I managed to configure my feign client to use a customSSLSocketFactory.
My use case: I have an API that requires SSL cert to be added to my local machine on JAVA cacerts in order to fetched it. However, I am unable to add this cert after deployment at the cloud server. Hence, I needed to configure my Feign client to take in a custom trust store from my application itself.
Here's my version of it
Create your own truststore with the certificate added
keytool -import -file <filepath-of-certificate> -alias <certificate-alias-name> -keystore <truststore-alias-name>
system will prompt for a password, set your own password and remember it
Truststore doesn't need any extensions for this case
Copy this truststore file to resource folder of your spring boot app
In application.properties or application.yml file set the classpath and password for your truststore. I'm using application.yml so the example as follows
e.g.
example-service:
server: https://example.api.com
trustStore: classpath:<Your truststore name>
trustStorePassword: yourpassword
You can also skip step 3 and directly pass in the truststore and password below
- In CustomFeignConfig class you can directly pass in the truststore as a resource and directly pass in the truststore inputstream to sslsocketfactory. Note that the Bean notation is essential to override the default feign config.
I only require a truststore for my case, so I pass in a empty new KeyManager[]{} on sslContext.init()
public class CustomFeignConfig {
@Bean
public Client feignClient(@Value("${example-service.trustStore}") Resource trustStoreResource,
@Value("${example-service.trustStorePassword}") String trustStorePassword) throws Exception {
try {
return new Client.Default(
sslSocketFactory(trustStoreResource.getInputStream(), trustStorePassword),
null);
} catch (Exception e) {
throw new Exception("Error in initializing feign client", e);
}
}
private static SSLSocketFactory sslSocketFactory(InputStream trustStoreStream, String trustStorePassword)
throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
TrustManagerFactory tmf = createTrustManager(trustStoreStream, trustStorePassword);
sslContext.init(new KeyManager[]{}, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
private static TrustManagerFactory createTrustManager(InputStream trustStoreStream, String trustStorePassword)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(trustStoreStream, trustStorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
return tmf;
}
}
- Apply this custom feign config only to the api that requires it
@FeignClient(name = "example-service", url = "${example-service.server}", configuration = CustomFeignConfig.class)
public interface MyFeignClient {}
acceptingTrustStrategy
you created. Did you intend to pass it to the sslContext? – Ungley