Running TLS 1.2 on Java 1.6 with Bouncy Castle

January 31, 2024

The Backstory

Back in 2020, I wrote a browser for a lightweight Internet protocol known as Gemini. It aims to hit a sweet spot between the commercialized, graphics-and-megabytes-heavy World Wide Web, and the text-based but limited Gopher. Generally, it's much more svelte than Gopher, and thus a good choice for lightweight devices. But it also requires modern cryptography, particularly TLS 1.2 (or 1.3).

This can be a bit limiting when it comes to running on older devices, as TLS 1.2 debuted in 2008. For me, writing my browser in Java, that meant that it was not easy to run on anything older than 2011's Java 7 - Java 6, from 2006, does not ship with TLS 1.2. Practically, this limits Java applications to Windows XP (2001) or newer, and on Mac, 2011's 10.7 with the Apple JDK, or 2009's 10.6 with OpenJDK.

But there was another option - the Legion of the Bouncy Castle. This is a cryptography library for many versions of Java.

Alas, their tagline "A fun place to stay, if you've got some time to kill" is perhaps more accurate than they intended. I took a stab at implementing Bouncy Castle on Java 1.6, which would add very-valuable-in-2020 Windows 2000 support, but found myself stymied, despite following the official guide and searching StackOverflow and various blogs. I like to think of myself as reasonably competent at encryption, having implemented Feistel ciphers in code and configured Jetty servers to prefer modern Galois Counter-Mode algorithms over older RSA algorithms in the 2010s, but the weeds were too deep for my attempt to be successful in 2020.

Success and How to Replicate

Looking again in 2024, a few people had asked new questions about Bouncy Castle on Stack Overflow, and combined with my previous attempts, the new suggestions were enough to get things working. I'll try to distill what I learned below, while staying high-level enough to make this code plug-and-play (which is what I really wanted all along.

Maven Configuration

I will assume you are using Maven, or can adjust Maven to Gradle. I found that I needed two libraries in my dependencies:

<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15to18</artifactId>
	<version>1.66</version>
</dependency>
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bctls-jdk15to18</artifactId>
	<version>1.66</version>
</dependency>
		

Nota bene: The last version of Maven that supports Java 1.6 is 3.2.5. Conversely, the first Maven release to support https for Maven Central by default, which is now required, is 3.2.3.

The first is the general Bouncy Castle Provider, for Java 1.5 to 1.8. Note that you must use the "jdk15to18" libraries, not "jdk15on", as otherwise you will encounter the error "TlsCryptoException: cannot calculate secret". I don't fully follow the reason, something about signature validation and perhaps an Oracle certificate expiring in 2021, but can confirm that switching from "jdk15on" to "jdk15to18" resolves this problem. Credit to this answer for figuring out this problem.

The second library is the Bouncy Castle TLS Provider, which is obviously required for this task. As far as I'm aware, newer versions of the library should work as well.

Export-Grade Cryptography

The next problem is export-grade cryptography. If you try running the code (next section), you will receive an error "java.security.InvalidKeyException: Illegal key size". This is because Java 1.6 ships with "export grade" cryptography, which is inadequate for secure modern servers.

To resolve this, you'll need to use a (free) Oracle Account to download the Java 1.6 Cryptography Extensions Unlimited Strength Policy Files, from this Oracle page. Unzip the files, and put them in the "jre/lib/security" folder of your JDK installation. Credit

Code

Once you have the right libraries and export-grade cryptography, and assuming you have the TrustManager set up to avoid the lovely PKIX path building errors we've all encoutnered while writing Java code, it's surprisingly simple. Before invoking any calls that will need TLS, add these two lines, which only need to be run once:

Security.addProvider(new BouncyCastleProvider());
Security.addProvider(new BouncyCastleJsseProvider());
		

These register the two providers that were imported via Maven, and because they implement the interfaces that Java provides for cryptography libraries, all you have to do is reigster them, and they can start working.

PKIX Configuration

Skipping over the TrustManager and PKIX is admittedly skipping a fair amount. For my application, not being a World Wide Web application, it was not necessary, and at any rate not the hold-up to getting Bouncy Castle working. But chapter three of Bouncy Castle's official guide does cover trust manager configuration.

Future Article Improvement Opportunities

This section is primarily for my own future reference, but also provide opportunities for readers who wish to write their own blog posts that build upon this one.

Conclusion

Once I knew how to configure it, BouncyCastle worked well, it was just figuring out how to configure it that was difficult. It's a large library, but thanks to complying to Java's built-in interface specifications, if all you need is TLS support on Java 1.6, it can be configured relatively easily following this guide - just make sure those unlimited strength crypto libraries are also present!


Return to Blog Index