Skip to content

Instantly share code, notes, and snippets.

@kannangce
Last active April 3, 2024 09:11
Show Gist options
  • Save kannangce/4fed4d42fb8c980c82623e3eed9413ef to your computer and use it in GitHub Desktop.
Save kannangce/4fed4d42fb8c980c82623e3eed9413ef to your computer and use it in GitHub Desktop.
Feign client for 1 or 2-way TLS with self signed certificates when javax.net.ssl.keyStore / javax.net.ssl.trustStore cannot be overridden.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.AccessController;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import feign.Client;
import feign.Feign;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.codec.ErrorDecoder;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
/**
* Class containing the utilities required to create SSL enabled fiegn clients for
* self-signed certificate servers
*
* @author kannan.r
*/
public class FeignSSLUtils
{
/**
* Returns the custom feign client that can be used for the TLS enabled
* communication using self signed certificates.
* <br/>
* Use cases:
* <ol>
* <li>The client needs to communicate with a system with self-signed
* CAs, with 1 or 2-way TLS</li>
* <li>And, we are not allowed to override the
* <i>javax.net.ssl.trustStore</i> and <i>javax.net.ssl.keyStore</i>
* properties.</li>
* </ol>
*
* @param theTrustStoreFile
* The truststore file using which the outgoing connections will
* be trusted.
* @param theKeyStoreFile
* The keystore file, for the peer to trust the outgoing. Can be
* null if the connection doesn't requires 2-way TLS.
* @param theKeyStorePassword
* The keystore password
* @return {@link Client} instance based on the given params
* @throws Exception
* When there is problem in initializing the client instance.
*/
public static Client getFeignClient(File theTrustStoreFile, File theKeyStoreFile,
String theKeyStorePassword) throws Exception
{
try
{
return new Client.Default(getClientSSLSocketFactory(theTrustStoreFile, theKeyStoreFile,
theKeyStorePassword), null);
} catch (Exception e)
{
throw new Exception("Error in initializing feign client", e);
}
}
/**
* Gets the {@link SSLSocketFactory} instance for the client communication
* using the given truststore file and password.
*
* @param theTrustStoreFile
* The truststore file using which the outgoing connections will
* be trusted.
* @param theKeyStoreFile
* The keystore file, for the peer to trust the outgoing. Can be
* null if the connection doesn't requires 2-way TLS.
* @param theKeyStorePassword
* The keystore passowrd
* @return TLS supported {@link SSLSocketFactory} instance with the given
* key and trust stores.
* @throws NoSuchAlgorithmException
* When the underlying security providers doesn't support TLS.
* @throws KeyStoreException
* If no Provider supports a KeyStoreSpi implementation for the
* specified type.
* @throws PrivilegedActionException
* When there is access issue with the truststore file
* @throws CertificateException
* If the certificates from the truststore is unable to be read
* @throws IOException
* When there is error in reading the truststore
* @throws KeyManagementException
* If the SSL context initialization fails
* @throws UnrecoverableKeyException
* When there is problem in using the given keystore file
*/
private static SSLSocketFactory getClientSSLSocketFactory(File theTrustStoreFile,
File theKeyStoreFile, String theKeyStorePassword)
throws NoSuchAlgorithmException, KeyStoreException, PrivilegedActionException,
CertificateException, IOException, KeyManagementException, UnrecoverableKeyException
{
// This supports TLSv1.2
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore tStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream tStorefile = getFileInputStream(theTrustStoreFile);
tStore.load(tStorefile, null);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(tStore);
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
KeyManager[] keyManagers = null;
if (theKeyStoreFile != null)
{
FileInputStream kStorefile = getFileInputStream(theKeyStoreFile);
kStore.load(kStorefile, theKeyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(kStore, theKeyStorePassword.toCharArray());
keyManagers = kmf.getKeyManagers();
}
if (keyManagers == null)
{
// 2-way TLS not required. Let JVM uses its default
keyManagers = new KeyManager[] {};
}
sslContext.init(keyManagers, 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 PrivilegedActionException
{
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;
}
}
});
}
}
@ajitdas91
Copy link

As per my requirement I am using different jks for feign and tomcat. But I am facing issue while connectivity that is while creating feign object, application is using correct jks but on run time its using different jks (picking tomcat jks as trust store).
we are having different jks for diffident connectivity please help me shutout the issue

@kannangce
Copy link
Author

If the client is instantiated with this method, probably that will never use JVMs truststore. Please ensure that your feign client is instantiated that way.

@ajitdas91
Copy link

Yes I have followed similar approach to create the client and with below additional code
@bean
@ConditionalOnProperty(name = "rest.ssl.enable", havingValue = "true")
public Client getFeignClient() throws Exception {
try {
return new Client.Default(getClientSSLSocketFactory(), null);
} catch (Exception e) {
throw new Exception("Error in initializing feign client", e);
}
}
rest code is similar to your code. After that i have autowared the client.

@kannangce
Copy link
Author

You're not passing the custom truststore,keystore file.

@ajitdas91
Copy link

I have not passed these values as parameter but used as injected value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment