Recently, I was testing transport level security (SSL) on Oracle's Container for Java (also part of OAS - Oracle Application Server).
My setting was: I had one web service bound to a secure site I had configured in its own xml and included in server.xml, which run on the standard 443 port (port choice is irrelevant to the problem anyway). It was all running fine until I decided to set needs-client-auth to "true" on the ssl-config tag in the secure web site configuration. What this setting does is requesting the client to also send a certificate to the server (apart from the one the server presents to the client since this is http + ssl). This is even if you don't configure client-cert authentication as the auth-method in the login-config tag of the web service's web.xml descriptor file. In fact, the problem I faced happens even if no authentication options (no login-config tag) are configured for the web service at all.
So I went ahead and enabled needs-client-auth, created my keystore (a JKS - Java Key Store - in this case) and certificates, imported the client certificate in my browser, accessed https://myserver which asked for the client certificate to present, selected the one I had imported and the welcome page for OC4J was shown. So far, so good.
After that, I accessed the test page for the web service I had deployed. The test page is an automatically generated page which allows you to input the arguments and call the web service. I entered some test values in the input fields... and where I usually got the SOAP response envelope I got either:
SSL Error: Received fatal alert: bad_certificate
or:
java.security.PrivilegedActionException: javax.xml.soap.SOAPException: Message send failed: Software caused connection abort: recv failed
along with the following message in the console:
WARNING IOException in ServerSocketAcceptHandler$AcceptHandlerHorse:run
I then looked into the log.xml file and found this stack trace for the IOException:
javax.net.ssl.SSLHandshakeException: null cert chain
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:188)
at com.sun.net.ssl.internal.ssl.ServerHandshaker.clientCertificate(ServerHandshaker.java:1256)
at com.sun.net.ssl.internal.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:159)
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 oracle.oc4j.network.ServerSocketAcceptHandler.doSSLHandShaking(ServerSocketAcceptHandler.java:245)
at oracle.oc4j.network.ServerSocketAcceptHandler$AcceptHandlerHorse.run(ServerSocketAcceptHandler.java:867)
at com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(ReleasableResourcePooledExecutor.java:298)
at java.lang.Thread.run(Thread.java:619)
notice the "null cert chain" part.
So, what was going on?
I also had an OC4J 10.1.2 installation, so I decided to give that one a try. I configured a similar secure web site, the same keystore in the secure web site's xml as I had used for the newer OC4J version, and deployed practically the same web service (accounting for minor differences as required by each OC4J version). Same thing, went to the test page... and it worked.
So I started thinking about what was going on. First, I noticed that the IOException and null cert chain were also logged when I chose to cancel the client certificate window in my browser (thus not presenting any certificate to the server) or when the server presented a certificate for which I had not added trust in my browser options (I was using my own CA - Certificate Authority), even for the welcome page at https://myserver. So somehow this was sounding like the client certificate was not really being sent.
Then, I looked into each OC4J web service test page code. OC4J 10.1.2 used a GET on the web service, while OC4J 10.1.3 presents a seemingly more elaborate interface which uses a POST. In essence, the test page in the newer version appears to be a complete rewrite.
Recapping, same keystore, same certificates... same web service. Variable: in general, the OC4J version; specifically: the web service test page. From that, I suspected the web service test page itself - I needed a different interface to check I was right.
Enter soapUI, http://www.soapui.org/ which offers an open source version that can be downloaded for free. Inside soapUI, go to File -> Preferences and configure your KeyStore (I tested JKS and PKCS12) and KeyStore Password, then create a New soapUI Project from the web service's WSDL. Try a request and... it works!
Take out the KeyStore from the settings and you'll see the web service complains it did not receive the certificate - as expected. Configure client-cert auth-method in the web service if desired - works as well.
In conclusion, something's fishy about the newer version web service's test page, it seems client certificates are lost in the way. But, since it works from a different interface, the server is correctly configured!
My suggestion: if you run into this problem, try soapUI to see if you configured things right.
No comments:
Post a Comment