Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add client certificates to the Spring WebClient?

I'm building a Spring WebClient which internally calls to REST API's which are hosted in different server. To do that I need to send public key (.cert) and private key (.key) to every request for the handshake. I'm not sure how to do that with Spring WebClient.

I tried setting up WebClient, but struck at adding this peace of code

WebClient Builder

this.webCLient = WebClient.builder()
                .baseUrl("https://some-rest-api.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString())
                .build();

Actual Call

this.webClient.get()
                .uri("/getData")
                .exchange()
                .flatMap(clientResponse -> {
                    System.out.println(clientResponse);
                    return clientResponse.bodyToMono(MyClass.class);
                });

Since there were no certificates added to the request, I'm getting the handshake error on the log

javax.net.ssl.SSLException: Received fatal alert: handshake_failure

How to add those certificates to the WebClient requests, so I don't get this error ? I have the certificates, but not sure how to add it.

like image 696
user3470629 Avatar asked Oct 01 '19 22:10

user3470629


2 Answers

It took me some time to find the missing piece in Thomas' answer.

Here it is:

public static SslContext getTwoWaySslContext() {
        
    try(FileInputStream keyStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(clientSslKeyStoreClassPath));
        FileInputStream trustStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(clientSslTrustStoreClassPath));   
        ) {
        KeyStore keyStore = KeyStore.getInstance("jks");
        keyStore.load(keyStoreFileInputStream, clientSslKeyStorePassword.toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, clientSslKeyStorePassword.toCharArray());
            
        KeyStore trustStore = KeyStore.getInstance("jks");
        trustStore.load(trustStoreFileInputStream, clientSslTrustStorePassword.toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        trustManagerFactory.init(trustStore);
            
        return SslContextBuilder.forClient()
            .keyManager(keyManagerFactory)
            .trustManager(trustManagerFactory)
            .build();
            
    } catch (Exception e) {
        log.error("An error has occurred: ", e);
    }
        
    return null;
}


HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(SslUtil.getTwoWaySslContext()));
ClientHttpConnector clientHttpConnector = new ReactorClientHttpConnector(httpClient);
WebClient webClient = webClientBuilder
    .clientConnector(clientHttpConnector)
    .baseUrl(baseUrl)
    .build();

Enjoy!

like image 162
jumping_monkey Avatar answered Oct 22 '22 19:10

jumping_monkey


taken from the documentation Spring Webclient - Reactor Netty

To access the ssl configurations you need to supply a custom netty HttpClient with a custom sslContext.

SslContext sslContext = SslContextBuilder
        .forClient()
        // build your ssl context here
        .build();

HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(sslContext));

WebClient webClient = WebClient.builder()
    .clientConnector(new ReactorClientHttpConnector(httpClient))
    .build();
like image 20
Toerktumlare Avatar answered Oct 22 '22 18:10

Toerktumlare