How to proxy to an SSL endpoint that expects client certificate for authentication.

4,072 views
Skip to first unread message

TDDWiremock

unread,
Oct 14, 2014, 10:30:08 AM10/14/14
to wiremo...@googlegroups.com

I am trying to create stub mappings by recording them while running wiremock as a proxy. I am running wiremock at a SSL port using a default certificate and proxying to an endpoint that expects client cetificate for authentication:

java -jar wiremock-1.50-standalone.jar --port 8080 --https-port 8182 --proxy-all="https://someserverendpoint" --preserve-host-header --record-mappings --verbose

I am not able to see any files in mappings and __files folder as I see the 'SSLHandshakeException'.

How do I get around this?

Any response is appreciated.

Tom Akehurst

unread,
Oct 14, 2014, 10:34:50 AM10/14/14
to wiremo...@googlegroups.com
I'd guess the problem is that your HTTP client isn't set up to trust WireMock's default SSL certificate, but I'd need to see the full stack trace to confirm that.

If this is the case, I'd suggest either pointing WireMock at your own trust store via the --https-keystore param, or configuring your HTTP client to trust SSL certs by default.

scottst...@xcelsior.io

unread,
Nov 17, 2014, 9:45:57 AM11/17/14
to wiremo...@googlegroups.com
I think I have a similar or the same issue -- the problem isn't with the client trusting the WireMock's cert but when WireMock makes its SSL request to a server requiring a client certificate.  The app service under test sets up its client cert with system props:

-Djava.security.egd=file:///dev/urandom -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.keyStore=/path/to/key/store -Djavax.net.ssl.keyStorePassword=secret

Using the --https-keystore parameter has no affect.  The keystore has a password other than "password", maybe that's it?

I played with HttpClientFactory a bit and could get WireMock to make its client connection and proxy fine with HttpClients.custom().useSystemProperties() but that's not ideal.

Am I doing something wrong or is this functionality not implemented?

Thanks!

Jay Zhu

unread,
Dec 3, 2014, 1:38:18 AM12/3/14
to wiremo...@googlegroups.com
I just submitted a pull request (https://github.com/tomakehurst/wiremock/pull/199) to add supporting for specifying truststore path/password in HttpsSettings class. This might be helpful in your case as well.

Thanks,
Jay

Tom Akehurst

unread,
Dec 6, 2014, 1:10:07 PM12/6/14
to wiremo...@googlegroups.com
Apologies, I misread the original post here.

However, coincidentally (or maybe not?) a new PR has been raised that adds support for client certs when proxing:
https://github.com/tomakehurst/wiremock/pull/201

Would this satisfy your requirements?

Tom Akehurst

unread,
Dec 6, 2014, 1:18:38 PM12/6/14
to wiremo...@googlegroups.com
And apologies again! Just catching up on my inbox, and I've noticed you've also opened a PR!

I'll try and review this asap.

Tom Akehurst

unread,
Dec 6, 2014, 5:12:12 PM12/6/14
to wiremo...@googlegroups.com
OK, by some strange coincidence there are now 2 PRs open to do almost exactly the same thing - add support for a trust store and for WireMock to require a client certificate. However, neither address the original issue raised on this thread, which I understand to be support for proxying to another service that requires a client certificate.

Anybody feel like contributing this also, while we're dealing with client cert support?

Ravi Brewster

unread,
May 6, 2015, 1:21:01 AM5/6/15
to wiremo...@googlegroups.com
All,

Any movement on this feature?  It seems like you have a workaround?  We need to proxy to a service that uses a SSL *and* a certificate to authenticate... We would like to not have to run SSL on the front end (inbound to wiremock) and only use SSL to connect to the proxy host and send the certificate.   Any timeline on implementing this feature?  we are in java 7 and 1.5.5 of wiremock.

-Ravi  

Ravi Brewster

unread,
May 7, 2015, 8:22:30 PM5/7/15
to wiremo...@googlegroups.com
So as a workaround, we got this to work by passing a keystore that contained the ssl cert and the client cert using the wiremock command line switches while also setting them as java system properties at startup.  Seems redundant but it worked.  We were forced to add the ssl port even though we didn't want to connect to wiremock using ssl, but we ignored that port and used the non-ssl port and were able to proxy to the remote host. 

Rahul Joshi

unread,
Feb 19, 2016, 7:46:18 PM2/19/16
to wiremock-user
scottst...@xcelsior.io, tom,
I am having the exact same problem . Has this issue been resolved? I am using the following command to start my wire mock standalone. 

java -jar wiremock-2.0.0-beta-standalone.jar --https-port 9999 --record-mappings --verbose --https-keystore /Users/rahul.joshi/Downloads/wiremock/server-key.jks --keystore-password password --https-truststore /Users/rahul.joshi/Downloads/wiremock/clients.jks  --truststore-password password --https-require-client-cert --proxy-all https://localhost:8488 --preserve-host-header 

My java test client makes calls on https://localhost:9999 . The handshake between the client and the wiremock server works fine. I even have the wireshark packets to proove it. 
However, the handshake between the wiremock server and my application target server  which is started on port 8488 does not work correctly. According to the wireshark capture it seems like the wiremock server is not sending over the client certificates to the application server during the "Certificate Client Key exchange step" of the handshake. 
I thought that if you start wiremock with --https-truststore then it should send the required certs to the application server.

Is this fixed in a later version of wiremock or something?  

BTW...great tool!

Tom Akehurst

unread,
Feb 21, 2016, 2:30:20 PM2/21/16
to wiremock-user
This has been a working feature of WireMock for quite a while. See this test case for an example:

Can I suggest double checking you're passing the correct trust store password? Perhaps it might also be worth programmatically setting WireMock up as in the test case linked to above and see if you can make it work that way.

Rahul Joshi

unread,
Feb 21, 2016, 9:58:17 PM2/21/16
to Tom Akehurst, wiremock-user
Thanks for the reply Tom, 
I think checking the "clients.jks" to make sure it contains the correct certs with the correct passwords is what i will do next. One thing i did want to mention was that my clients.jks contains 3 certs.  It is basically a union of the truststores on the client and target application server. I will check to see if i have created this truststore correctly. 
If that does not work i guess the next option like you said is just programmatically setup wiremock so that I can do some debugging. 
Thanks!
Rahul

 


--
You received this message because you are subscribed to a topic in the Google Groups "wiremock-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/wiremock-user/X-VgopdJ7t4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to wiremock-use...@googlegroups.com.
To post to this group, send email to wiremo...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/wiremock-user/7dbd832c-b98c-403b-af2c-8853eac9810f%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Tom Akehurst

unread,
Feb 22, 2016, 3:58:28 AM2/22/16
to wiremock-user
I've never tried using a trust store containing more than one cert so I'll be interested to know if that's the problem.

Rahul Joshi

unread,
Mar 7, 2016, 3:18:47 PM3/7/16
to wiremock-user
Tom,
Sorry for the late reply on this. 
So I went ahead and tried the test case like you said with my own truststore and key store that contains multiple certs and multiple keys. the test case you pointed out works for this. So not an issue there. 

However, I think the problem I am having is to do with the proxy-all. I started wirmockserver runner this time by programmatically setting it up. 

task customWireMockRunner(type: JavaExec) {
 classpath
= sourceSets.main.runtimeClasspath

 main
= 'com.github.tomakehurst.wiremock.standalone.WireMockServerRunner'

 
// arguments to pass to the application
 args
= ["--https-port=9999",
 
"--record-mappings=true" , "--verbose=true",
 
"--https-keystore=/Users/rahul.joshi/Downloads/wiremock/wiremocktrust.jks",
 
"--keystore-password=password",
 
"--https-truststore=/Users/rahul.joshi/Downloads/wiremock/wiremocktrust.jks",
 
"--truststore-password=password",
 
"--https-require-client-cert=true",
 
"--proxy-all=https://localhost:8488",
 
"--preserve-host-header=true"]
 jvmArgs
= ["-Djavax.net.debug=all", "-Djavax.net.ssl.trustStore=/Users/rahul.joshi/Downloads/wiremock/wiremocktrust.jks"]
}


I run my test and I get the following error during the "Change Cipher Spec" step between wire mock server and my application server that i am proxying to. 

java.lang.RuntimeException: java.net.SocketException: Broken pipe
at com.github.tomakehurst.wiremock.http.ProxyResponseRenderer.render(ProxyResponseRenderer.java:85)
at com.github.tomakehurst.wiremock.http.StubResponseRenderer.render(StubResponseRenderer.java:47)
at com.github.tomakehurst.wiremock.http.AbstractRequestHandler.handle(AbstractRequestHandler.java:40)
at com.github.tomakehurst.wiremock.jetty6.Jetty6HandlerDispatchingServlet.service(Jetty6HandlerDispatchingServlet.java:98)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:401)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
at org.mortbay.jetty.security.SslSocketConnector$SslConnection.run(SslSocketConnector.java:713)
at com.github.tomakehurst.wiremock.jetty6.DelayableSslSocketConnector$1.run(DelayableSslSocketConnector.java:52)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:431)
at sun.security.ssl.OutputRecord.write(OutputRecord.java:417)
at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:876)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:847)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:717)
at sun.security.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:1077)
at sun.security.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:1222)
at sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1134)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:348)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.Protocexecute(ProtocolExec.java:184)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
at com.github.tomakehurst.wiremock.http.ProxyResponseRenderer.render(ProxyResponseRenderer.java:76)
... 19 more


As you can see the the "ProxyResponseRenderer" is causing the error. I am assuming that this is because my application server has already closed the connection or something. Does wire mock not support certain protocols? We are using TLSv1.2 in our jetty application server.

Tom Akehurst

unread,
Mar 8, 2016, 4:02:17 AM3/8/16
to wiremock-user
I doubt this is a protocol support issue as you'd see an exception that pointed to this explicitly.

Other than double checking that you are attempting to connect to the target system on the right port (https, not http) I can only suggest breaking out tcpdump to confirm whether the app is severing the connection.

Rahul Joshi

unread,
Mar 8, 2016, 3:47:32 PM3/8/16
to wiremock-user
Tom,
I fixed the issue. Following is what was happening: 
The wiremock proxy client was sending the first available cert from my truststore to the application server during the "Certificate, Client Key Exchange"  step of the SSL handshake. 
This cert however was not present in the truststore that was being used by the server. 
I am wondering if it would be worth it to have this in the HttpClientFactory in wire mock in the "buildSSLContextWithTrustStore" method.

                    .loadKeyMaterial(trustStore, trustStoreSettings.password().toCharArray(), new PrivateKeyStrategy() {
                      @Override
                      public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
                        return "<alias name passed from command line option only for proxy>";
                      }
                    })


if you think its worth it I can submit a pull request and then we can discuss the details in the pull request. In the mean time I have a work around to create the appropriate truststore and keystores just for my recording usecase. 

My main goal is to automate all this so that I can use wiremock to create our own internal framework for creating REST contract tests. I plan to just make a gradle plugin to do all of this.

Tom Akehurst

unread,
Mar 8, 2016, 4:21:48 PM3/8/16
to wiremock-user
Thanks for sharing your solution.

I agree it'd be useful to be able to use trust stores containing more than one cert so I'd welcome a PR.

Reply all
Reply to author
Forward
0 new messages