Make Feign client to take truststore from custom property
I ended up handcrafting my own instance of SSLSocketFactory
that I pass to my Feign client, using the below code,
/**
* Gets the {@link SSLSocketFactory} instance for the client communication
* using the given truststore file and password.
*
* Since the instance is used as client, this is instantiated with empty
* keystore and the truststore represented by the given truststore file.
*
*
* @param theTrustStoreFile
* The complete file path of the truststore.
* @return {@link SSLSocketFactory} instance that internally uses the given
* truststore.
* @throws Exception
* When there is an error in the creating the
* {@link SSLSocketFactory} instance.
*/
public static SSLSocketFactory getClientSSLSocketFactory(File theTrustStoreFile)
throws Exception
{
// This supports TLSv1.2
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream file = getFileInputStream(theTrustStoreFile);
kStore.load(file, null);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(kStore);
sslContext.init(new KeyManager[] {}, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
/**
* Reads the file into {@link FileInputStream} instance.
*
* @param file
* The file to be read.
* @return {@link FileInputStream} that represents the file content/
* @throws Exception
* When there is any error in reading the file.
*/
private static FileInputStream getFileInputStream(final File file) throws Exception
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<FileInputStream>()
{
@Override
public FileInputStream run() throws Exception
{
try
{
if (file.exists())
{
return new FileInputStream(file);
} else
{
return null;
}
} catch (FileNotFoundException e)
{
// couldn't find it, oh well.
return null;
}
}
});
}
And when I instantiate my client, I do it like,
Feign.builder().client(getClientSSLSocketFactory(trustFile),null)...
This gist contains sample code and its usage.
This is how i used FeignClient with keystore
and truststore
FeignClient Configuration
@Configuration
public class TestClientConfig {
@Bean
public Client feignClient() {
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Do your validations
return true;
}
};
String allPassword = "123456";
SSLContext sslContext = SSLContextBuilder
.create()
// .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.p12"), allPassword.toCharArray(), allPassword.toCharArray())
.loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray())
.build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
Interface
@FeignClient(name = "Test", url = "https://localhost:8443",configuration=TestClientConfig.class)
public interface TestClient {
@RequestMapping(method = RequestMethod.GET,value = "/hello")
String getHello();
}