Recently at work I was in need of connecting to a web service exposed via
HTTPS. I’ve been doing this from inside Servicemix 3.3.1 , which may seem
a bit inhibiting, but that was a requirement. Nevertheless I’ve been
trying my luck with the included servicemix-http-2008.01
component. I’ve created a simple Service
Unit using that component and made connection attempt.
Unfortunately I’ve encountered issues with the SSL conversation
negotiation. I had to dig deeper into the servicemix-http code
to find out these had something to do with my JCE
keystore. Read more to find out what happened!
Ok, so I had my xbean.xml for http component looking like
this:
<beans xmlns:http="http://servicemix.apache.org/http/1.0"
xmlns:my="http://example.pl/abc">
<http:endpoint
endpoint="default"
locationURI="https://abc.host.pl"
role="provider"
service="my:service-1"
soap="true"
soapVersion="1.1">
<http:ssl>
<http:sslParameters
keyStore="classpath:keystore.jks"
keyStorePassword="servicemix"/>
</http:ssl>
</http:endpoint>
</beans>
As you can see this is a proxy adapter to some outside service
exposed via secured HTTP protocol. Since it’s HTTPS I’ve
specified some SSL parameters. It was sufficient in my case to just pass
the keystore file and it’s password.
I’ve created my keystore.jks file in smx_home/conf
with password servicemix in the following manner:
keytool -genkey -alias alias -keyalg RSA -keysize 1024 -dname "CN=Marcin Cylke, OU=Java Security, O=ABC, c=PL" -keypass secret_pass -storepass secret_pass -KeyStore /tmp/new
You can see what’s in this file with this command:
keytool -list -v -KeyStore keystore.jks
At this point I thought, that having a configured keystore and my
component would suffice. Wrong! As soon as I’ve tried to connect to the
external service I got an exception:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1623)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:198)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:192)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1074)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:128)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:529)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:465)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1120)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:623)
at com.sun.net.ssl.internal.ssl.AppOutputStream.write(AppOutputStream.java:59)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
at java.io.FilterOutputStream.flush(FilterOutputStream.java:123)
at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:502)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1973)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:397)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:346)
at org.apache.servicemix.http.processors.ProviderProcessor.process(ProviderProcessor.java:167)
at org.apache.servicemix.soap.SoapEndpoint.process(SoapEndpoint.java:368)
at org.apache.servicemix.common.AsyncBaseLifeCycle.doProcess(AsyncBaseLifeCycle.java:600)
at org.apache.servicemix.common.AsyncBaseLifeCycle.processExchange(AsyncBaseLifeCycle.java:554)
at org.apache.servicemix.common.AsyncBaseLifeCycle.onMessageExchange(AsyncBaseLifeCycle.java:510)
at org.apache.servicemix.common.SyncLifeCycleWrapper.onMessageExchange(SyncLifeCycleWrapper.java:60)
at org.apache.servicemix.jbi.messaging.DeliveryChannelImpl.processInBound(DeliveryChannelImpl.java:620)
at org.apache.servicemix.jbi.nmr.flow.AbstractFlow.doRouting(AbstractFlow.java:172)
at org.apache.servicemix.jbi.nmr.flow.seda.SedaFlow.doRouting(SedaFlow.java:168)
at org.apache.servicemix.jbi.nmr.flow.seda.SedaQueue$1.run(SedaQueue.java:134)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:294)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:200)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1053)
... 30 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:289)
... 36 more
Hmmm.. this looks pretty nasty, but it’s not that bad. As one can
read here ,
it’s associated with the other site’s having an untrusted (unsigned)
certificate. Assuming you actually trust the other end of the
communication and this situation is ok for you, you should add the
servers certificate to your keystore. The previously mentioned link
contained a little java class that would do just that. You can find it
here (original code) InstallCert.java
or you can look into my slightly changed version here at github .
You should call it as follows, assuming that file
keystore.jks is in the current directory:
What you’ll probably see, when you execute this app is this:
mcl@correspondence:~/Desktop$ java InstallCert abc.host.pl keystore_pass
Loading KeyStore keystore.jks...
Opening connection to abc.host.pl:443...
Starting SSL handshake...
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1623)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:198)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:192)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1074)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:128)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:529)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:465)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1120)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1147)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1131)
at InstallCert.main(InstallCert.java:82)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:294)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:200)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:177)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1066)
... 8 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:289)
... 14 more
Server sent 1 certificate(s):
1 Subject CN=abc.host.pl, OU=IT, O=Tytus De z o.o., L=Lubien, ST=Wielkopolskie, C=PL
Issuer CN=abc.host.pl, OU=IT, O=Tytus De z o.o., L=Lubien, ST=Wielkopolskie, C=PL
sha1 18 20 0f 4d 75 05 4b 38 61 fc 62 ba 03 0d 28 8c 50 8f e4 bd
md5 19 cc d0 aa 6d 25 4f 05 d7 c8 40 0c 14 7b 90 a2
Enter certificate to add to trusted keystore or 'q' to quit: [1]
1
[
[
Version: V3
Subject: CN=abc.host.pl, OU=IT, O=Tytus De z o.o., L=Lubien, ST=Wielkopolskie, C=PL
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
modulus: 132273155309282144973195320716811175193976134222571452308768552101191894740218986214811429267243902443501737048442287069471714463675362773263911735240842200098922910391008625926103368536307544892982972025285316457425020882189531886424398004920996956401109864350477607702600482953541993347423082215862581179547
public exponent: 65537
Validity: [From: Mon Nov 30 14:06:24 CET 2009,
To: Thu Nov 28 14:06:24 CET 2019]
Issuer: CN=abc.host.pl, OU=IT, O=Tytus De z o.o., L=Lubien, ST=Wielkopolskie, C=PL
SerialNumber: [ 00]
]
Algorithm: [SHA1withRSA]
Signature:
0000: 40 75 D4 B0 85 D1 34 DE 09 24 3C BC 72 46 B2 E0 @u....4..$<.rF..
0010: 18 15 84 DD A2 EB BA 5A 9F E3 5C F0 8F 53 60 EC .......Z..\..S`.
0020: 2C 5D CB C1 EE C3 3A 65 CF 9B 6C E5 FC 01 DD 05 ,]....:e..l.....
0030: EA DC 66 BF FB 91 02 BC 39 77 CB 34 BC 7F 0B 23 ..f.....9w.4...#
0040: 40 C9 85 B3 2A 2A 20 AE 74 B0 C9 FB 47 5F B3 88 @...** .t...G_..
0050: E4 5E 6D 24 2F C0 43 9D 69 D5 69 4D 76 31 0A 62 .^m$/.C.i.iMv1.b
0060: 1A C1 25 FB 14 41 06 0E 9F A8 D1 75 DD B8 B2 B2 ..%..A.....u....
0070: 1D BF 90 11 69 18 9A C8 D5 AA 5D 26 6B 1C FB B0 ....i.....]&k...
]
Please note that there is a prompt (Enter certificate to add to
trusted keystore… ) in which you can enter the certificate number
you wish to add to your keystore.
After all those steps my request got through and I could happily
query HTTPS service as long as I wanted to! Great!
Possible problems
In my search for this problem’s solution I’ve encountered this kind
of exception:
java.lang.Exception: java.security.UnrecoverableKeyException: Cannot recover key
at org.apache.servicemix.http.processors.ConsumerProcessor.process(ConsumerProcessor.java:216)
at org.apache.servicemix.http.HttpBridgeServlet.doPost(HttpBridgeServlet.java:71)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:363)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:757)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
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:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:502)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:371)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.jetty.nio.SelectChannelConnector$RetryContinuation.run(SelectChannelConnector.java:525)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:451)
Caused by: java.security.UnrecoverableKeyException: Cannot recover key
at sun.security.provider.KeyProtector.recover(KeyProtector.java:311)
at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:121)
at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java:38)
at java.security.KeyStore.getKey(KeyStore.java:763)
at com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl.<init>(SunX509KeyManagerImpl.java:113)
at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl$SunX509.engineInit(KeyManagerFactoryImpl.java:48)
at javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:239)
at org.apache.servicemix.http.processors.CommonsHttpSSLSocketFactory.createUnmanagedFactory(CommonsHttpSSLSocketFactory.java:117)
at org.apache.servicemix.http.processors.CommonsHttpSSLSocketFactory.<init>(CommonsHttpSSLSocketFactory.java:50)
at org.apache.servicemix.http.processors.ProviderProcessor.getHostConfiguration(ProviderProcessor.java:276)
at org.apache.servicemix.http.processors.ProviderProcessor.process(ProviderProcessor.java:167)
at org.apache.servicemix.soap.SoapEndpoint.process(SoapEndpoint.java:368)
at org.apache.servicemix.common.AsyncBaseLifeCycle.doProcess(AsyncBaseLifeCycle.java:600)
at org.apache.servicemix.common.AsyncBaseLifeCycle.processExchange(AsyncBaseLifeCycle.java:554)
at org.apache.servicemix.common.AsyncBaseLifeCycle.onMessageExchange(AsyncBaseLifeCycle.java:510)
at org.apache.servicemix.common.SyncLifeCycleWrapper.onMessageExchange(SyncLifeCycleWrapper.java:60)
at org.apache.servicemix.jbi.messaging.DeliveryChannelImpl.processInBound(DeliveryChannelImpl.java:620)
at org.apache.servicemix.jbi.nmr.flow.AbstractFlow.doRouting(AbstractFlow.java:172)
at org.apache.servicemix.jbi.nmr.flow.seda.SedaFlow.doRouting(SedaFlow.java:168)
at org.apache.servicemix.jbi.nmr.flow.seda.SedaQueue$1.run(SedaQueue.java:134)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
A little googling led me to this StackOverflow question .
It seems that you cannot have multiple keys with different passwords
in the same keystore and use KeyManagerFactory class. Oh
well…
.
Ending
To sum up, the solution given works, but in my opinion using the
InstallCert.java app is rather dirty. I’ve been wondering, do
you know other ways of doing that thing?