RestTemplate request with certificate - spring

application.yaml has a certificate as a secret. How to make a request with restTemplate using certificate?
how to create a bean? What should the configuration look like?

Related

Spring Boot WebCLient accept Self Signed Certificate, but not with InsecureTrustManagerFactory

I'm trying to build a REST client using Spring Boot and utilizing WebClient, however I'm conflicted when trying to config HTTPS call to a REST API.
When using RestTemplate, I was able to get self signed certificate working by using TrustSelfSignedStrategy(), thus even when the certificate is self signed, it is still being validated for its hostname, expiry date, etc.
In WebClient, so far I only found the way self signed certificate is by utilizing InsecureTrustManagerFactory, however this will cause the whole validation to be skipped as well, effectively void the purpose of using HTTPS in the first place.
As quoted from Netty documentations:
An insecure TrustManagerFactory that trusts all X.509 certificates without any verification.
NOTE: Never use this TrustManagerFactory in production. It is purely for testing purposes, and thus it is very insecure.
Is there any way I can use self signed certificate in WebClient without having to dismantle all the verification?
Yes, you can use a self signed certificate. What you need to do is add the self signed certificate into a java keystore and load it into your application by getting the keystore and transforming it into a TrustManager. Afterword you can supply the TrustManager to the SslContextBuilder which is needed for configuring the WebClient based on Netty. See below for an example:
Path truststorePath = Paths.get(/path/to/your/truststore)
InputStream truststoreInputStream = Files.newInputStream(truststorePath, StandardOpenOption.READ)
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
truststore.load(truststoreInputStream, truststorePassword);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(truststore);
SslContext sslContext = SslContextBuilder.forClient()
.trustManager(trustManagerFactory)
.build()
HttpClient httpClient = HttpClient.create()
.secure(sslSpec -> sslSpec.sslContext(sslContext));
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build()

What type of certificate spring-boot application needs to have HTTPS connection?

I would like to make clients require HTTPS protocol to connect to my spring-boot backend application but I have no idea what kind of certificate I need for this.
Any certificate is good for this? Or I need to "turn on" some configurations?
Well, best you can do is to generate self-signed certificate.
There are many examples.
Also, you have to configure your backend.
Add dependency:
implementation 'org.apache.httpcomponents:httpclient:4.5'
Provide RestTemplate bean:
#Bean
private RestTemplate restTemplate() {
SSLContext sslContext = buildSslContext();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
private SSLContext buildSslContext() {
try {
char[] keyStorePassword = sslProperties.getKeyStorePassword();
return new SSLContextBuilder()
.loadKeyMaterial(
KeyStore.getInstance(new File(sslProperties.getKeyStore()), keyStorePassword),
keyStorePassword
).build();
} catch (Exception ex) {
throw new IllegalStateException("Unable to instantiate SSL context", ex);
} finally {
sslProperties.setKeyStorePassword(null);
sslProperties.setTrustStorePassword(null);
}
}
Provide required SSL properties in your application.properties or application.yaml file:
server:
ssl:
enabled: true
key-store: /path/to/key.keystore
key-store-password: password
key-alias: alias
trust-store: /path/to/truststore
trust-store-password: password
That's it. Now you can see your Tomcat is starting on 8080 (or another port) (https).
Alternatively, you can use my spring boot starter
1, You need a certificate for the application
there is two kind of certificates: self-signed certificate and ca-issued certificate
self-signed certificate
you can sign a certificate yourself use keytool
self-signed certificate has some disadvantage, browers will show a red alert page when access self-sign certificate site, and client need import the corresponding self-sign ca root certificate to communicate with server
ca-issued certificate
you can get a ca-issued certificate from let's encrypt
ca-issued certificate do not have all those disadvantage mentioned above ,it just work
2, You also need setting some spring boot configurations, see Spring Boot Docs - How to configure ssl

How can I authenticate a Ribbon load balancer and Zuul proxy using a certificate?

I have a Spring application, that acts as an authentication proxy for two backend servers. A user will access the Spring application and be forwarded to the backend once he is successfully authenticated. To prevent unwanted access without prior authentication the backend servers require a certificate as authentication.
My Spring application uses Netflix-Ribbon as a load balancer and Netflix-Zuul as a Proxy for the users requests. How can I configure them to use the client certificate that is required for the authentication on the backend servers?
Ok, I figured it out. You can configure your own CloasableHttpClient as a #Bean and create a custom SSL context. You can provide a certificate to a server through .loadKeyMaterial(). Zuul will then use these settings.
#Configuration
public class HttpClientConfig {
#Bean
public CloseableHttpClient httpClient() throws Throwable {
String keyPassphrase = "yourCertificatePassword";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("Path/to/your/clientCert.pfx"), keyPassphrase.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, keyPassphrase.toCharArray())
.build();
return HttpClients.custom()
.setSSLContext(sslContext)
.build();
}
}

Add Spring Sleuth to Spring Oauth2's requests

We have problem with propagation of traceId in requests which are called by spring oauth2 module.
For instance consider authorization and resource server. In resource server we have spring security configuration to ensure get rsa public key from authorization server with following property:
security:
oauth2:
resource:
jwk:
key-set-uri: http://authorization-server:8080/key-set
When I call controller of resource server with jwt token, I can see in zipkin traces from resource server and authorization server as well, but there is no traceId propagation from resource server to authorization server.
First record is calling rest api to get resources, and second record is produced call to authorization server to find out public RSA key.

Certificate Based authentication, client share certificate to server

I have a requirement from a server application to share a SSL certificate. step i did:
I generated a self signed certificate against the Domain IP address(don't have domain name) where my application is deployed.
i shared the certificate to the server. they will keep the certificate in their trust store.
server is validating the request for the IP address. if the request are not coming from the IP address they are stopping them.
My question:
i have a spring boot application. do i need to make any change in my code for the certificate i have generated. if yes then what is the change.
Yes you need to make changes in your code. You need to load your keystore(with keypair) and if required also load your truststore into your http client. Most of the http clients require a SSLContext, so this would be sufficient for you:
KeyStore keyStore = ...;
TrustStore trustStore = ...;
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
// Spring provides by default RestTemplate as HTTP Client, this client is an Apache HTTP Client wrapper
// The setup would be:
HttpClient httpClient = HttpClient.newBuilder();
.sslContext(sslFactory.getSslContext());
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory)

Resources