Skip to content

Instantly share code, notes, and snippets.

@samiraghayarov
Created June 16, 2021 13:32
Show Gist options
  • Save samiraghayarov/fc400e18b4abf19caa4bb80ce771100c to your computer and use it in GitHub Desktop.
Save samiraghayarov/fc400e18b4abf19caa4bb80ce771100c to your computer and use it in GitHub Desktop.
package net.tarabutgateway.obc.bobf.accounts.repository;
import feign.Client;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import feign.httpclient.ApacheHttpClient;
import net.tarabutgateway.obc.bobf.accounts.config.MTLSConfiguration;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
public class BobfFeignTLSConfiguration {
@Value("${feign.client.config.default.connectTimeout}")
private int connectTimeout;
@Value("${feign.client.config.default.readTimeout}")
private int readTimeout;
private static final Logger LOGGER = LoggerFactory.getLogger(BobfFeignTLSConfiguration.class);
@Bean
Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) {
return new SpringFormEncoder(new SpringEncoder(converters));
}
@Bean
Client getFeignClient(MTLSConfiguration mtlsConfiguration) {
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(readTimeout).setSocketTimeout(mtlsConfiguration.getReadTimeout()).build();
HttpClientBuilder httpClientBuilder = HttpClients.custom().setDefaultRequestConfig(requestConfig);
if ("true".equalsIgnoreCase(mtlsConfiguration.getNoopHostnameVerifier())) {
httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); // **THIS IS VERY BAD**
}
return new ApacheHttpClient(httpClientBuilder.setSSLContext(getSSLContext(mtlsConfiguration)).build());
}
private SSLContext getSSLContext(MTLSConfiguration mtlsConfiguration) {
// this is an in-memory key store, use password if there is one configured
String password = Optional.ofNullable(mtlsConfiguration.getKeyStorePassword()).orElse("");
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new IllegalArgumentException("Error initialising MTLS certificate factory", e);
}
try {
SSLContextBuilder sslContextBuilder = SSLContexts.custom();
if (new File(mtlsConfiguration.getKeyStore()).exists())
sslContextBuilder = sslContextBuilder.loadKeyMaterial(
getIdKeyStore(certificateFactory, mtlsConfiguration.getKeyStore(), password),
password.toCharArray(), (aliases, socket) -> mtlsConfiguration.getAlias());
File ts = new File(mtlsConfiguration.getTrustStore());
if (ts.exists()) {
KeyStore trustStore = getTrustStore(mtlsConfiguration.getTrustStore(),
mtlsConfiguration.getTrustStorePassword());
sslContextBuilder = sslContextBuilder.loadTrustMaterial(trustStore, null);
}
return sslContextBuilder.build();
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("Error initialising MTLS SSL Context", e);
}
}
private KeyStore getIdKeyStore(CertificateFactory certificateFactory, String keyStore, String password)
throws IOException, GeneralSecurityException {
// prepare the key store for mutual-tls
KeyStore identityKeyStore = KeyStore.getInstance("jks");
identityKeyStore.load(null);
Set<String> privateKeys = Stream.of(requireNonNull(new File(keyStore).listFiles()))
.filter(file -> !file.isDirectory())
.map(File::getName)
.filter(f -> f.endsWith(".pem"))
.collect(Collectors.toSet());
privateKeys.forEach(pemKey -> {
String alias = pemKey.split("\\.")[0];
String keyFile = keyStore + pemKey;
String certFile = keyStore + alias + ".crt";
if (new File(certFile).exists()) {
try (FileInputStream input = new FileInputStream(certFile)) {
X509Certificate kcert = (X509Certificate) certificateFactory.generateCertificate(input);
identityKeyStore.setKeyEntry(alias, getPrivateKey(keyFile), password.toCharArray(),
new Certificate[]{kcert});
LOGGER.info("added to keystore : {} with alias", alias);
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("Error initialising the key store for mtls", e);
}
} else {
LOGGER.warn("ignoring the Key {}, cannot add to keystore without the crt", keyFile);
}
});
return identityKeyStore;
}
private KeyStore getTrustStore(String trustStore, String trustStorePassword)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
// load the truststore with the default jdk cacerts
KeyStore trustKeyStore = KeyStore.getInstance("jks");
try (FileInputStream in = new FileInputStream(System.getProperty("java.home") + "/lib/security/cacerts")) {
trustKeyStore.load(in, null);
}
// append tg ca certs
try {
try (FileInputStream stream = new FileInputStream(trustStore)) {
trustKeyStore.load(stream, trustStorePassword.toCharArray());
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.warn("truststore load failed");
}
Enumeration<String> enumeration = trustKeyStore.aliases();
while (enumeration.hasMoreElements())
LOGGER.info("added to truststore: {}", enumeration.nextElement());
return trustKeyStore;
}
private RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
String privateKeyPEM = getKey(filename);
privateKeyPEM = privateKeyPEM.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
PKCS8EncodedKeySpec spec1 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyPEM));
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec1);
}
private String getKey(String fileName) throws IOException {
File file = new File(fileName);
return com.google.common.io.Files.asCharSource(file, StandardCharsets.UTF_8).read();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment