Last active
April 3, 2024 09:11
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
}); | |
} | |
} |
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.
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.
You're not passing the custom truststore,keystore file.
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
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