Last active
March 25, 2021 16:32
-
-
Save serac/4f77e42c1ac515114a1c8e9469be25ad to your computer and use it in GitHub Desktop.
Configuring RestTemplate for Client TLS in a Spring Boot Application
This file contains hidden or 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
/* | |
* See LICENSE for licensing and NOTICE for copyright. | |
*/ | |
package edu.vt.middleware.app; | |
import java.io.File; | |
import java.security.*; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.function.Predicate; | |
import java.util.stream.Collectors; | |
import javax.net.SocketFactory; | |
import javax.net.ssl.SSLContext; | |
import org.apache.http.client.HttpClient; | |
import org.apache.http.config.Registry; | |
import org.apache.http.config.RegistryBuilder; | |
import org.apache.http.conn.scheme.PlainSocketFactory; | |
import org.apache.http.conn.scheme.Scheme; | |
import org.apache.http.conn.scheme.SchemeRegistry; | |
import org.apache.http.conn.socket.ConnectionSocketFactory; | |
import org.apache.http.conn.socket.PlainConnectionSocketFactory; | |
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |
import org.apache.http.impl.client.HttpClients; | |
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | |
import org.apache.http.ssl.SSLContexts; | |
import org.cryptacular.bean.KeyStoreFactoryBean; | |
import org.cryptacular.io.ClassPathResource; | |
import org.cryptacular.io.FileResource; | |
import org.cryptacular.io.Resource; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | |
import org.springframework.web.client.RestTemplate; | |
/** | |
* Application entry point. | |
* | |
* @author Marvin S. Addison | |
*/ | |
@SpringBootApplication | |
public class Application { | |
/** Logger instance. */ | |
private final Logger logger = LoggerFactory.getLogger(Application.class); | |
/** Application HTTP client connection pool size. */ | |
@Value("${application.httpclient.pool.size:10}") | |
private int httpClientPoolSize; | |
/** Application HTTP client keepalive duration in seconds. */ | |
@Value("${application.httpclient.keepalive:120}") | |
private int httpClientKeepAlive; | |
/** Application keystore path. */ | |
@Value("${application.keystore.path}") | |
private String keystorePath; | |
/** Application keystore type. */ | |
@Value("${application.keystore.type}") | |
private String keystoreType; | |
/** Application keystore password. */ | |
@Value("${application.keystore.password}") | |
private String keystorePassword; | |
/** Keystore alias for application client credential. */ | |
@Value("${application.keystore.entry.app-name}") | |
private String applicationKeyAlias; | |
public static void main(String[] args) { | |
LoggerFactory.getLogger(Application.class).info("Starting application"); | |
SpringApplication.run(Application.class, args); | |
} | |
@Bean | |
public RestTemplate restTemplate() { | |
return new RestTemplate(new HttpComponentsClientHttpRequestFactory(createHttpClient(applicationKeyAlias))); | |
} | |
private static Resource makeResource(final String path) { | |
if (path.startsWith(FILE_RESOURCE_PREFIX)) { | |
return new FileResource(new File(path.substring(FILE_RESOURCE_PREFIX.length()))); | |
} else if (path.startsWith(CLASSPATH_RESOURCE_PREFIX)) { | |
return new ClassPathResource(path.substring(CLASSPATH_RESOURCE_PREFIX.length())); | |
} | |
// Assume a path without a known prefix is a file | |
return new FileResource(new File(path)); | |
} | |
private HttpClient createHttpClient(final String keyAlias) { | |
logger.info("Creating HTTP client using keystore={} and alias={}", keystorePath, keyAlias); | |
final KeyStore trustStore = new KeyStoreFactoryBean( | |
makeResource("classpath:/truststore.jks"), "JKS", "changeit").newInstance(); | |
final KeyStore keyStore = new KeyStoreFactoryBean( | |
makeResource(keystorePath), keystoreType, keystorePassword).newInstance(); | |
final SSLContext sslContext; | |
try { | |
sslContext = SSLContexts.custom() | |
.loadKeyMaterial(keyStore, keystorePassword.toCharArray(), (aliases, socket) -> keyAlias) | |
.loadTrustMaterial(trustStore, (x509Certificates, s) -> false) | |
.build(); | |
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) { | |
throw new IllegalStateException("Error loading key or trust material", e); | |
} | |
final SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( | |
sslContext, | |
new String[] { "TLSv1.2" }, | |
null, | |
SSLConnectionSocketFactory.getDefaultHostnameVerifier()); | |
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | |
.register("http", PlainConnectionSocketFactory.getSocketFactory()) | |
.register("https", sslSocketFactory) | |
.build(); | |
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); | |
connectionManager.setMaxTotal(httpClientPoolSize); | |
connectionManager.setDefaultMaxPerRoute(httpClientPoolSize); | |
return HttpClients.custom() | |
.setSSLSocketFactory(sslSocketFactory) | |
.setConnectionManager(connectionManager) | |
.build(); | |
} | |
} |
This file contains hidden or 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
*** CertificateRequest | |
Cert Types: RSA, DSS, ECDSA | |
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA | |
Cert Authorities: | |
<C=US, DC=edu, DC=vt, O=Virginia Polytechnic Institute and State University, CN=Virginia Tech Middleware CA> | |
*** ServerHelloDone | |
matching alias: somebody | |
*** Certificate chain | |
chain [0] = [ | |
[ | |
Version: V3 | |
Subject: C=US, DC=edu, DC=vt, ST=Virginia, L=Blacksburg, O=Virginia Polytechnic Institute and State University, OU=Middleware-Client, OU=Middleware, SERIALNUMBER=OU812, CN=somebody | |
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11 |
what is x509Certificates ??
@nullmicgo x509Certificates is an agrument from the lambda.
Wha is applicationKeyAlias?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I should note that this is for http-components 4.5.2. I'm sure it will be incorrect on the next minor release :(
@apache/httpclient, we appreciate using @deprecated, but let's be reasonable.