Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringBoot: FeignClient with SSL (p12)

I'm trying to create a FeignClient for one external HTTP API which uses SSL. The struggle is - how to modify default Spring FeignClient with my logic, in this case SSL Connection Factory. So basically I wanna keep all the good things Spring automatically does for the FeignClients, like Hystrix, Sleuth tracing, etc and make it work with my SSL factory.

Will appreciate any suggestions.


Here is what I tried to do:

I've tried to provide a custom @Configuration outside of ComponentScan:

@Configuration
public class CustomFeignConfiguration
{
    @Bean
    public Feign.Builder feignBuilder()
    {
        Client trustSSLSockets = new Client.Default(
                TrustingSSLSocketFactory.get("server1"),
                new NoopHostnameVerifier());

        log.info("feignBuilder called");
        return Feign.builder().client(trustSSLSockets);
    }
...
}

Made FeignClient use it through annotation

    @FeignClient(name = "sslClient", configuration = CustomFeignConfiguration.class, url = "https://...")

Where "TrustingSSLSocketFactory" is implemented similar to this.

Now if I inject my client in a Spring application, I can see that "feignBuilder" was called and it successfully loaded my keys. The problem is that the created client doesn't actually use the specified SSLFactory for createSocket calls. So I get:

Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.8.0_72]
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154) ~[na:1.8.0_72]
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2023) ~[na:1.8.0_72]
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125) ~[na:1.8.0_72]
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_72]
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_72]
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_72]
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) ~[na:1.8.0_72]
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) ~[na:1.8.0_72]
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513) ~[na:1.8.0_72]
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.8.0_72]
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_72]
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338) ~[na:1.8.0_72]
    at feign.Client$Default.convertResponse(Client.java:152) ~[feign-core-9.3.1.jar:na]
like image 319
Val Avatar asked Nov 28 '16 21:11

Val


2 Answers

Create the Client as it's own @Bean rather than part of the builder. builder.client(client) gets called later on, overriding the client you set while creating the builder.

So

@Bean
public Client feignClient()
{
    Client trustSSLSockets = new Client.Default(
            TrustingSSLSocketFactory.get("server1"),
            new NoopHostnameVerifier());

    log.info("feignClient called");
    return trustSSLSockets;
}
like image 147
spencergibb Avatar answered Nov 03 '22 16:11

spencergibb


I did something like this which served my purpose-

@Bean
public Client feignClient()
{
    Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
    log.info("feignClient called"); 
    return trustSSLSockets;
}


private SSLSocketFactory getSSLSocketFactory() {
    try {
    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            //Do your validations
            return true;
        }
        };

        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        return sslContext.getSocketFactory();
    } catch (Exception exception) {
        throw new RuntimeException(exception);
    }
}
like image 37
xomoni Avatar answered Nov 03 '22 14:11

xomoni