I have a Java application that is talking HTTPS with some server I don't have access to... How can I capture the traffic so that I can see the contents of the requests and responses?

This post is Part 1 of a series. If you are interested in node.js apps instead, see part 2!

Such was my situation at work recently. If we simply run tcpdump on the server where the Java application is running, we will get a packet capture, yes. But we will only be able to see the session initiation of the TLS protocol. We won't be able to access the HTTP protocol which is wrapped inside the strong encryption of the TLS session.

Wireshark and the (Pre)-Master-Secret/SSLKEYLOGFILE

If you are a Wireshark veteran, you may have already seen Mr. Jim Shaver's excellent post on the subject: Decrypting TLS Browser Traffic With Wireshark – The Easy Way*, or at least become familiar with the (Pre)-Master-Secret logging method (usually called SSLKEYLOGFILE in web browsers).

If you haven't done this before and you haven't read Jim's article, I recommend skimming it before you continue here.

*EDIT: I re-hosted the wayback machine version of this article since its no longer online.

This method works by logging important fragments of the key-exchange part of the TLS protocol in a somewhat standardized format that Wireshark understands. The details will vary depending on the cipher suite that the client and server agree on using, but the gist of it remains the same: In the typical use case, TLS works by using asymmetric cryptography (certificates, trust chains, public/private keys, etc) to achieve two things.

  1. The client receives a proof that it is talking to someone who possesses a key that was signed by a Certificate Authority that it trusts.
  2. The client and server construct a shared secret which is known to both, but is too hard/impossible for a nosy observer to guess without having access to the Private Keys involved.

Then the TLS protocol proceeds by using symmetric encryption (AES) based on that shared secret. I believe this is called the "Master Secret". The method I am talking about here centers around collecting and logging this master secret or some information which will allow Wireshark to calculate it, then importing that log file into Wireshark alongside the packet capture.

The Wireshark Documentation on the Master Secret method of decrypting TLS packet captures links to a project named jsslkeylog. The code and compiled jar file is avaliable for download from SourceForge.net, but I much preferred to read the code and compile it myself before running it, thus I built it myself and uploaded it to Github. Especially because whether knowingly or unknowingly, SourceForge has been known to distribute malware in the past.

It's an Agent, Neo!

Author's Rendition of what a Java Agent might look like

At any rate, jsslkeylog will do what we need. It has a neat trick up its sleeve — it is actually a Java Agent. The Java Agent / Java instrumentation API is a special feature of the JVM which allows users to load extra code into a java application, and grant that code access to re-write the application's Java Bytecode on the fly while the application runs. Running a Java application with a Java Agent doesn't require access to the original source code and it doesn't require us to re-compile anything. Java Agents are usually used for instrumentation, for example New Relic's flagship application monitoring product for Java uses a Java Agent.

jsslkeylog modifies the standard implementation of TLS in the Java Standard Library in such a way that it will log the TLS master secret information to a file, just like the SSLKEYLOGFILE setting in a web browser would.

Notes from my experience

In my particular use case, the server has an initialization script which runs the Java application:

/usr/bin/java ${JAVA_OPTS} -jar myApplication.jar

I simply copied the jSSLKeyLog.jar file I built into my home directory on the server and replaced this with:

/usr/bin/java ${JAVA_OPTS} -javaagent:/home/fjohnson/jSSLKeyLog.jar=/home/fjohnson/SSLKEYLOG -jar myApplication.jar

This will cause jsslkeylog to modify the existing java code so that it writes wireshark-formatted master key information to the file /home/fjohnson/SSLKEYLOG

After restarting the application, I started the packet capture using the tcpdump utility which was already installed on Ubuntu:

tcpdump -i any -s 65535 -w /home/fjohnson/my-capture.pcap

Finally, after downloading the my-capture.pcap and SSLKEYLOG files, I was able to open my-capture.pcap in Wireshark and set SSLKEYLOG as the (Pre)-Master-Secret log file in Wireshark's SSL protocol settings.

Unfortunately, something went wrong in my case. Either due to some incompatibility, bugs, or something, Wireshark didn't immediately display the decrypted http protocol in the main window.

After consulting the troubleshooting docs, I configured wireshark to log its SSL decryption Debug information to a file — see the screenshot. Inside that log file, I found the decrypted payloads I was looking for.

Personally, I have only used this trick where the Java Application on which I loaded the agent is the Client. I have no idea if this will work for an application which is serving HTTPS.