Showing posts with label jsse. Show all posts
Showing posts with label jsse. Show all posts

Monday, December 12, 2011

Bad Certificate in WAS 5.1.0 (IBM JDK 1.4.1)

Once upon a time, there was a legacy web application system handling loans processing. It ran on Windows 2000 but used IBM WebSphere Application Server (WAS) 5.1.0 installation (yes, no fixpacks!). It ran well until recently when the system tried to access a remote URL using HTTPS/SSL, an error occurred. The log files were investigated and to the horrified faces of the support personnel...

javax.net.ssl.SSLHandshakeException: bad certificate

A few recommendations here and there led to them summoning me to have a look at this issue. At first I thought it was just a simple case of not importing the SSL certificates into the keystore. But after using ikeyman (WAS tool) to do the necessary, it still doesn't work. Ok, time to Google...

Version 5.1.0 of WAS runs on IBM JDK 1.4.1 (not even 1.4.2), which made matters worse. A quick search using Google yielded 2 most relevant results:

Problem is, both links didn't actually provide any solution, but they did nudge me a little to a workaround which I am documenting here right now.

This problem is not common if the WAS installations are patched up to at least v5.1.1, but due to the system's legacy status and most likely be replaced by another system early next year, there was no real incentive to patch it and 'hope for the best'. The personnel of the vendor supporting the system was also a blur case contract worker who spew common buzzwords yet lack any substance and logic.

So, what to do, what to do??? There were no source codes available (the vendor decided not to give an earlier contract worker his time off, so he retaliated by deleting all the source codes...a case of coder gone cuckoo/crazy). So, I decided to decompile the source codes using JD-GUI (http://java.decompiler.free.fr/?q=jdgui) after learning from the IT support girl on the URL that triggers the remote host handshaking. A quick look at the web.xml file and the servlet responsible for the remote request was identified.

After looking through the servlet code, I was able to identify the root cause and reconfirmed it by writing some JUnit tests:
  • Test #1 - Using Sun JDK1.4.2, no imported certs: Fail, "unknown certificate" (expected)
  • Test #2 - Using Sun JDK1.4.2, with imported certs: Pass (expected)
  • Test #3 - Using IBM JDK1.4.1 (similar to WAS 5.1.0), no imported certs: Fail (expected)
  • Test #4 - Using IBM JDK1.4.1 (similar to WAS 5.1.0), with imported certs: Fail, "bad certificate" (expected)

So the problem is the JDK, not the application code (sort of). IBM JDK1.4.1 is using IBM JSSE v1 (not even v2). I wanted to try using IBM JSSE v2 API files, but after looking through several Google results, no one was triumphant doing this.

To call the remote URL using HTTPS/SSL, Apache HttpClient API was used. A quick look at the online guide (http://hc.apache.org/httpclient-3.x/sslguide.html) gave me an idea to replace the default class to handle HTTPS connections with a custom class which can be coded to use Sun JSSE implementation. To do this, some class-overriding was necessary. The final code for the socket factory (I had to dig through the Sun JSSE classes to find the implementation class which actually is 'internal' and not recommended to be referenced externally, but desperate times require desperate measures):


import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;

import com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl;

public class SunJsseSslSocketFactory implements SecureProtocolSocketFactory {

 public Socket createSocket(String host, int port) 
  throws IOException, UnknownHostException
 {
  SSLSocketFactoryImpl sfi = new SSLSocketFactoryImpl();
  return sfi.createSocket(host, port);
 }

 public Socket createSocket(String host, int port, 
  InetAddress clientHost, int clientPort) 
   throws IOException, UnknownHostException
 {
  SSLSocketFactoryImpl sfi = new SSLSocketFactoryImpl();
  return sfi.createSocket(host, port, clientHost, clientPort);
 }

 public Socket createSocket(Socket socket, String host, 
  int port, boolean autoClose) 
   throws IOException, UnknownHostException
 {
  SSLSocketFactoryImpl sfi = new SSLSocketFactoryImpl();
  return sfi.createSocket(socket, host, port, autoClose);
 }

}


Then, to modify the HttpClient call to use this new socket factory class:

Protocol myHTTPS = new Protocol( "https", new SunJsseSslSocketFactory(), url.getPort() );

This line is placed before any calls to the remote URL, and only needed to be executed once.

2 other steps to do to complete the fix:
  • Place the Sun JSSE file (jsse.jar) into [WAS_HOME]/java/jre/lib/ext
  • Add the Sun JSSE provider into file [WAS_HOME]/java/jre/lib/security/java.security:

security.provider.6=com.sun.net.ssl.internal.ssl.Provider

In the end, the fix was applied to the production boxes and the remote call finally worked. All credit goes to the HttpClient API makers who at least coded some hooks for this customisation.

Saturday, January 03, 2009

Hacking your way to smoother SSL testing: Follow up #1

Yup, after a long "break" from blogging, I have restarted my "engines" to blog about this topic. Decided to write more on this before my memory starts failing...

Back in 2006, I applied the short-circuit codes to go around using HTTPS without having the proper certificates imported to the local truststore...was doing some POC for sending SMS requests via web services to an SMS gateway provider. But the SMS sender is just a standalone Java program. The hack worked nicely.

Returning to the future (present day, 2009). But security has its price...'real' SSL certificates (even testing, self-signed ones) have an expiry. When the testing team must shift the server dates earlier or later to "sync" with the mainframe data, this may cause the certificates to "prematurely expire". Sure, just regenerate the self-signed cert and whack it back into the truststore. The "good" way to do it. Or just use HTTP, code's still the same, just a parameter change. "Oh but this won't simulate closely to the production environment. If something goes wrong because you skipped something, will you be able to bear the brunt???", says the support manager...and if testing team finds out then there will be ICBMs flying around (aka email wars).

But I am just not a "normal, well-adjusted" coder. Always trying to find ways to circumvent stuff :)...

So, I tried to apply the hack (previous post) to see if it still works in WebSphere Application Server (WAS). The DummySSLSocketFactory class have to be refactored to use javax.net equivalents of the Sun-specific classes used by the Java tip article (http://www.javaworld.com/javatips/jw-javatip115.html). First I tried it on a standalone program with a few JDKs:
  • Sun JDK 1.4.2_18 - Failed (Noted in previous post, but after 2 years passed, I still have verify this)
  • IBM JDK 1.4.1 (comes with WSAD) - Passed
  • Sun JDK 1.6.0_10 - Passed
OK, WAS 5.1 is running on IBM JDK 1.4, so chances of success is high. I fitted the code to set the security property to use DummySSLSocketFactory in a startup class in my webapp. Added a logging statement to ensure that this code has been executed during server startup. Then the code to call a HTTPS web service is triggered....FAILED!

Hmm, strange. I suspected that the security property set failed to take effect. I looked up the javadocs for javax.net.ssl.SSLSocketFactory. There's a method "getDefault()" to get the default SSL socket factory object. Went back to the standalone test program and called this method and print the class name out before setting the security property and after setting it.

The output looked like this:
Before: com.ibm.jsse2.SSLSocketFactoryImpl
After: com.ibm.jsse2.SSLSocketFactoryImpl

Ahhhh....so calling SSLSocketFactory.getDefault() will render the security property setting to none effect. The above output is similar when testing the webapp in WAS...no effect.

So, there must be something "magical" going on in this SSLSocketFactory class. Ok, let's look at the source code. But the code isn't there in the JDK (src.zip). I can't believe my eyes, why is it absent in the uber source code zip file??? It's also missing in Sun JDK 1.6.

Well, maybe Google can give me some answers. Some folks are also puzzled by this, and there's even a bug submission (yeah, Bug ID 4811569, take a look: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4811569). Is it fixed? Nah, it's not a bug, evaluation:

Not a bug. What's in javax.net is part of the Security Code (JSSE) therefore, the sources are not available through normal means for export control reasons.
However, it is possible to get access to it through the SCSL process.
See http://wwws.sun.com/software/java2/download.html

OK lo, I'll go look them up sometime later.

So, the hack is not all-encompassing, after all, it's just a hack. Maybe if there's a way to inject a certificate into a truststore without the hassle of running keytool or ikeyman...sometimes the keytool doesn't work (due to a vendor solution putting in extra jars into the WAS JRE's lib/ext folder and screwing up the tool, and there's no other JDK/JRE lying around...ya, just install a clean JDK and use it...sure, but it's production environment, and I can't just install anything I like, and there are more than a dozen machines running similar applications...things get unnecessarily complicated when you are not in total control), and the only means of communicating with the machines in a DMZ is using SSH via VPN, which somehow is not able to display GUIs (is there a way, or maybe not allowed by firewall?). So, learn the ikeyman command line version, or find out how to inject certs programmatically?

I chose the way of the code...stay tuned for the next follow-up post...

Yes, I re-iterate, I am not a "normal, well-adjusted" coder.

Wednesday, March 08, 2006

Hacking your way to smoother SSL testing

I referred to this post (http://www.javaworld.com/javatips/jw-javatip115.html) which has 2 cool files (DummySSLSocketFactory.java & DummyTrustManager.java) that enable an application to accept all certificates WITHOUT validation, even if the certificate name differs from the server name. At your application, add the following statement (preferably just as the program starts):

Security.setProperty( "ssl.SocketFactory.provider", "DummySSLSocketFactory");

Yeah, this is coooool....

But unfortunately, this doesn't work in JDK 1.4.2. Excerpt from http://java.sun.com/j2se/1.4.2/docs/relnotes/features.html#security:

The JSSE implementation provided in this release includes strong cipher suites. However, due to U.S. export control restrictions, this release does not allow alternate "pluggable" SSL/TLS implementations to be used.

Aiii, need to do those keytool import/export/genkey stuff.....

But anyway, using JDK 1.5, it works! But I wonder, has US export restrictions got lax??? Kekekeke...but don't bother doing this in your startup script:

-Dssl.SocketFactory.provider=DummySSLSocketFactory

Coz it doesn't work!!! Stop wasting your time :p