Hi everyone, I'm trying to call via Wiremock an ex...
# wiremock-java
a
Hi everyone, I'm trying to call via Wiremock an external endpoint protected by mutual authentication; here the config:
Copy code
wireMockConfiguration = options()
                        .disableRequestJournal()
                        .usingFilesUnderDirectory(rootFolder)
                        .port(Integer.parseInt(getProperty("port")))
                        .adminAuthenticator(getAdminAuthenticator())
                        .jettyAcceptors(Integer.parseInt(getPropertyOrDefault("acceptor.threads", DEFAULT_ACCEPTOR_THREADS)))
                        .keystorePath(getProperty("keystore.path"))
                        .keystorePassword(getProperty("keystore.password"))
                        .trustStorePath(getProperty("truststore.path"))
                        .trustStorePassword(getProperty("truststore.password"))
                        .extensions(
                                new AdminRequestLoggingFilter(),
                                new ServiceRequestLoggingFilter(),
                                new ServiceResponseLoggingFilter(),
                                new ResponseTemplateTransformer(true));
Here the error:
Copy code
SL failure trying to make a proxied request from WireMock to ...
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My keystore contains my private cert and the truststore contains the server certificate (everything ok with curl). Is there something missing? Thanks Alessandro
r
The exception suggests that your trust store does not contain the necessary certificates to communicate with the remote server. Have you tried writing a simple main method that uses the java http client to call the remote server using your trust store?
a
Hi @Rob Elliot, yes it works perfectly with the same keystore and truststore.
Is there a way to override the default HttpClientFactory?
<https://github.com/wiremock/wiremock/blob/2.35.0/src/main/java/com/github/tomakehurst/wiremock/http/HttpClientFactory.java>
r
BTW are you doing client certificates as well as server certificates? The WireMock client doesn't do client certificates I'm afraid
No, there's no easy way to inject a different HttpClientFactory sadly - it's constructed statically in
ProxyResponseRenderer
🙁
a
I think the problem is about the server certificates, since calling other endpoints with the certificates previously trusted in cacerts, everything is ok. So it seems that the client cert are correctly used by Wiremock, but not the server ones :-(
r
Can you share your test main method? Presumably the trust store only contains public certs, so you could perhaps share that too?
a
Yes I can, but the endpoint is not testable since the domain is not exposed on Internet
Copy code
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;

public class MutualAuthHttpClient {
    private static final String KEYSTORE_TYPE = "JKS";
    private static final String KEYSTORE_PASSWORD = "password";
    private static final String TRUSTSTORE_TYPE = "JKS";
    private static final String TRUSTSTORE_PASSWORD = "password";
    private static final String KEY_MANAGER_ALGORITHM = "SunX509";
    private static final String SSL_CONTEXT_PROTOCOL = "TLS";

    public static void main(String[] args) throws Exception {
        String endpointUrl = "endpoint";
        String keystoreFilePath = "path/to/keystore";
        String truststoreFilePath = "path/to/keystore";

        KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
        keyStore.load(new FileInputStream(keystoreFilePath), KEYSTORE_PASSWORD.toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KEY_MANAGER_ALGORITHM);
        keyManagerFactory.init(keyStore, KEYSTORE_PASSWORD.toCharArray());

        KeyStore trustStore = KeyStore.getInstance(TRUSTSTORE_TYPE);
        trustStore.load(new FileInputStream(truststoreFilePath), TRUSTSTORE_PASSWORD.toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KEY_MANAGER_ALGORITHM);
        trustManagerFactory.init(trustStore);

        SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_PROTOCOL);
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

        HttpsURLConnection.setDefaultHostnameVerifier((String hostname, SSLSession session) -> true);

        URL url = new URL(endpointUrl);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setDoOutput(true);

        
        DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.flush();
        outputStream.close();

        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        System.out.println(response.toString());
    }
}
r
It looks like you're using WireMock as a reverse proxy, not a forward/browser proxy, correct?
(i.e. you haven't called
enableBrowserProxying(true)
when you configured WireMock, so presumably instead you are configuring a specific stub to proxy to the endpoint, and calling WireMock directly)
a
Yes, using it in this way: just proxying a specific endpoint configured in /mappings.
r
That’s particularly odd because WireMock should not validate certs when doing that…
(The theory is that you know what you’re doing when you explicitly proxy to an endpoint and privately signed certs are very common in testing)
a
@Rob Elliot sorry but I didn't get it: the way I use WM is actually not the right one? Anyway thanks for your support :-)
r
It might be worth seeing if it will work without the trust store settings… I’ll have a look when I get in.
a
This is the only way I make it working:
Copy code
wireMockConfiguration = options()
                        .disableRequestJournal()
                        .usingFilesUnderDirectory(rootFolder)
                        .port(Integer.parseInt(getProperty("port")))
                        .adminAuthenticator(getAdminAuthenticator())
                        .jettyAcceptors(Integer.parseInt(getPropertyOrDefault("acceptor.threads", DEFAULT_ACCEPTOR_THREADS)))
                        .keystorePath(getProperty("keystore.path"))
                        .keystorePassword(getProperty("keystore.password"))
                        .trustStorePath(getProperty("keystore.path"))
                        .trustStorePassword(getProperty("keystore.password"))
                        //.notifier(new ConsoleNotifier(true))
                        .extensions(
                                new AdminRequestLoggingFilter(),
                                new ServiceRequestLoggingFilter(),
                                new ServiceResponseLoggingFilter(),
                                new ResponseTemplateTransformer(true));
I forced the truststore reading the keystore and I trusted the root CA in my cacerts. If I don't set the truststore (even wrong) it doesn't work throwing this error:
Copy code
SSL failure trying to make a proxied request from WireMock to *endpoint*
Received fatal alert: handshake_failure
I found a similar problem here: https://groups.google.com/g/wiremock-user/c/SugVgCVypss
Hi @Tom, any suggestion? Thanks in advance
Hi @Tom, @Rob Elliot have you read this thread? https://groups.google.com/g/wiremock-user/c/SugVgCVypss
r
Ah, sorry, I missed the "mutual authentication" in the original. Hence why you are using reverse proxying and why you still get TLS errors despite WireMock ignoring them. The diagnosis in that thread seems fairly sound You could raise an issue with a PR and some good tests and we could look to fixing it. I can't make any promises on timescale though I'm afraid.
a
Hi @Rob Elliot, thank you very much for the support; I'm looking forward to do it 🙂