How to ignore SSL cert trust errors in Feign?

Bee picture Bee · Apr 17, 2017 · Viewed 18.7k times · Source

How can I achieve curl -k in feign client?

I know I can do this. Just want to know if there's a way to ignore or disable.

new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)

Answer

Dan Smith picture Dan Smith · Jun 21, 2017

Disclaimer

You should not actually do this for a number of very good reasons. The easiest way to fix SSL issues is to actually follow SSL best practices and use valid certificates. There are excellent projects online such as https://letsencrypt.org/ that will even allow you to get great security for free if the host is publicly accessible (if it has a real hostname that can be verified against).

USE AT YOUR OWN RISK. MAKE SURE YOU UNDERSTAND YOU ARE VIOLATING A LOT OF BEST PRACTICES AND ONLY USE THIS IF YOU UNDERSTAND THAT.

If you cause some type of major problem using this example code, you are liable.

Real talk

I had the same problem dealing with internal (publicly non-accessible) services that I wanted to call from a spring-boot application and I solved it using the following code.

Brief Overview

A great many people will tell you that you can either accept all certificates, hard-code your particular cert in it, or something else. You can actually allow only certain trusted hosts through the codepath, which is what I am attempting here for an additional layer of security.

In this example code, you can pass multiple hosts to the classes and it should allow requests to only those hosts to be issued with invalid certificates, and everything else will go through the normal chain of command.

This is not really production grade code, but hopefully you will get some use out of it.

Enough lecturing, what follows may interest you the most.

The Code

This is using for Java 8 and spring-boot.

Configuration

@Configuration
    public class FeignClientConfiguration {

    @Bean
    public Client client() throws NoSuchAlgorithmException, 
        KeyManagementException {

        return new Client.Default(
            new NaiveSSLSocketFactory("your.host.here"),
            new NaiveHostnameVerifier("your.host.here"));
    }
}

NaiveHostnameVerifier

public class NaiveHostnameVerifier implements HostnameVerifier {
    private final Set<String> naivelyTrustedHostnames;

    private final HostnameVerifier hostnameVerifier =
        HttpsURLConnection.getDefaultHostnameVerifier();

    public NaiveHostnameVerifier(String ... naivelyTrustedHostnames) {
        this.naivelyTrustedHostnames =
                Collections.unmodifiableSet(
                    new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
    }

    @Override
    public boolean verify(String hostname, SSLSession session) {
        return naivelyTrustedHostnames.contains(hostname) ||
                hostnameVerifier.verify(hostname, session);
    }
}

NaiveSSLSocketFactory

public class NaiveSSLSocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory sslSocketFactory = 
                    (SSLSocketFactory) SSLSocketFactory.getDefault();

    private final SSLContext alwaysAllowSslContext;
    private final Set<String> naivelyTrustedHostnames;

    public NaiveSSLSocketFactory(String ... naivelyTrustedHostnames) 
        throws NoSuchAlgorithmException, KeyManagementException {

        this.naivelyTrustedHostnames = 
                Collections.unmodifiableSet(
                    new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));

        alwaysAllowSslContext = SSLContext.getInstance("TLS");
        TrustManager tm = new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) 
                throws CertificateException {}

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                    return null;
            }
        };

        alwaysAllowSslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return sslSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return sslSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(socket, host, port, autoClose) 
            : sslSocketFactory.createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port) 
            : sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localAddress, localPort) 
            : sslSocketFactory.createSocket(host, port, localAddress, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return (naivelyTrustedHostnames.contains(host.getHostName())) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port) 
            : sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
        return (naivelyTrustedHostnames.contains(host.getHostName())) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localHost, localPort) 
            : sslSocketFactory.createSocket(host, port, localHost, localPort);
    }
}

References

I borrowed heavily from this answer:

Trusting all certificates using HttpClient over HTTPS