Java’s SSLContext protocol name is a footgun

This should be old news, but I keep seeing the same mistake crop up, so I thought I’d blog about it and spread awareness. In Java, if you want to configure TLS you generally start with an SSLContext. And you get an instance of this class by calling the static method SSLContext.getInstance("TLSv1.3"), specifying the version of the protocol you want to support. But typically a TLS connection supports other versions of the protocol, so what exactly does specifying “TLSv1.3” here mean? Probably not what you think it means…

The Java Security Standard Algorithm Names document doesn’t say much useful: “Supports […] TLS version 1.3; may support other SSL/TLS versions.” Well, which other versions? Later ones or earlier ones? That seems kind of important. It’s even vaguer if you don’t specify a version – the generic “TLS” identifier is specified as (my emphasis):

Supports some version of TLS; may support other SSL/TLS versions.

Useful.

OK, but what about the JSSE chapter in the Java Security Developer’s Guide. That seems at first glance to be more precise:

Like other JCA provider-based engine classes, SSLContext objects are created using the getInstance() factory methods of the SSLContext class. These static methods each return an instance that implements at least the requested secure socket protocol. The returned instance may implement other protocols, too. For example, getInstance("TLSv1") may return an instance that implements TLSv1, TLSv1.1, and TLSv1.2.

OK, that sounds promising! So, if I specify a version then that is taken as a minimum version and I may also get more recent versions? Great, sign me up!

Except, that is the exact fucking opposite of what the default SunJSSE provider does! When you specify “TLSv1.1” (for example), what the default provider does is treat that as a maximum TLS version. So the resulting SSLContext supports all versions of TLS up to (and including) 1.1, but nothing later. So if you have old code that requests version 1.1 and you try to connect to a modern server that only supports 1.2 and 1.3, then you’ll get a connection failure. And in modern Java, this will fail earlier because TLS 1.1 is disabled by default. If you specify “TLSv1.2” then you’ll just silently get a downgraded protocol for no good reason at all, when you probably thought you were being good and specifying a sensible minimum version.

It’s not just the default provider that does this, lots of others have followed the lead, including e.g., the Conscrypt/BoringSSL provider used by Android.

I suspect this behaviour exists because of a fear of breaking poorly-written software that baulks at unknown versions and doesn’t handle downgrades properly. But the problem is that many developers think it is best practice to specify a version when creating an SSLContext, and some security scanners even tell you to do so. In the best case, you then get code that is secure until the next major hole is discovered in TLS and v1.4 gets released. In the worst case you’ve silently implemented a self-inflicted protocol downgrade attack. I wonder how many Java apps were (and maybe still are) only supporting TLS 1.1/1.0 despite the underlying JDK supporting 1.2 or even 1.3?

Aside

I should stop here and mention a subtlety: this behaviour applies to client TLS connections only. Server-side SSL contexts completely ignore the protocol you specify here for the most part and go ahead and support everything.

So what should you do instead? Well there’s really no good answer here. Probably the best thing to do is to use the generic “TLS” identifier, which gets you an unspecified version of TLS, but which all providers I’ve looked at so far interpret as “sensible modern protocol versions”, i.e., TLS 1.3 and 1.2 (with 1.1 and earlier supported but disabled by default). There’s no guarantee at all of that behaviour, but there’s also no guarantee when you specify a version, so pick your poison.

(I’ve raised a bug for this, as it finally pissed me off enough, but my guess is they’ll either ignore it or fix the guide to be as vague as the standard names descriptions).

#Java #JSSE #Security #tls
Security Developer’s Guide

The Java Secure Socket Extension (JSSE) enables secure Internet communications. It provides a framework and an implementation for a Java version of the TLS and DTLS protocols and includes functionality for data encryption, server authentication, message integrity, and optional client authentication.

Oracle Help Center

Java и постквантовый TLS

В JDK 27 появится JEP 527: гибридный post-quantum key exchange для TLS 1.3. Разбираем, что меняется в JSSE, зачем нужен X25519MLKEM768 и какие проблемы могут всплыть при миграции. А ты готов к квантовым атакам ?

https://habr.com/ru/articles/1038654/

#Java #JDK_27 #JEP_527 #TLS_13 #JSSE #PostQuantum_Cryptography #MLKEM #Hybrid_Key_Exchange #X25519MLKEM768 #javaxnetssl

Java и постквантовый TLS

Привет, Хабр! JEP 527 добавляет в JDK 27 поддержку post-quantum hybrid key exchange для TLS 1.3. Если приложение на стандартном JSSE-стеке через javax.net.ssl , то после перехода на JDK 27 с JEP 527...

Хабр
🎉 JSSE: Because who doesn't want a JavaScript engine built by an agent with the finesse of a toddler armed with a crayon? 🚀 Six weeks of pure, unadulterated hubris later, it's only just good enough to pass the tests that matter absolutely nowhere! 🤖✨
https://p.ocmatos.com/blog/jsse-a-javascript-engine-built-by-an-agent.html #JSSE #JavaScriptEngine #toddlerHubris #codingHumor #developerCommunity #HackerNews #ngated
What's in a vow? A language for the future of agentic coding - Notes & Code

Announcing Vow, an agent-first programming language with formal verification built in. Self-hosted, early, and open for you to try.

Notes & Code
What's in a vow? A language for the future of agentic coding - Notes & Code

Announcing Vow, an agent-first programming language with formal verification built in. Self-hosted, early, and open for you to try.

Notes & Code

Java TLS has been a blind spot for eBPF-based #observability tools for years. JSSE runs entirely inside the JVM with no exported symbols and nothing to attach to.

Here is how to solve it: https://coroot.com/blog/java-tls-instrumentation-with-ebpf/

Benchmarking our eBPF Java TLS agent against OpenTelemetry, Coroot added only +15% CPU overhead vs +38% from OTEL, which dropped 20% of requests under load.

#opensource #FOSS #OpenTelemetry #eBPF #Monitoring #SysAdmins #Linux #Java #JVM #JSSE #DevOps #Kubernetes #SRE