Cfimap SSLHandshakeException on 7.0.4.34/Java 21/x86

Architecture: Java OpenJDK 21, Tomcat 10, Linux, x86 Architecture
Version: Lucee 7.0.4.34

Error:
cfimap tag throws SSLHandshakeException: No appropriate protocol when connecting to IMAPS (port 993) [eg: GMail]

Description
cfimap with secure=true on port 993 fails with SSLHandshakeException: No appropriate protocol on Java 21 x86 Linux.

The same Lucee build and Java version on ARM Ubuntu 24.04 does not exhibit the problem.

Using JavaMail Session API directly with mail.imaps.ssl.protocols explicitly set does work correctly.

Trace
javax.mail.MessagingException: No appropriate protocol
(protocol is disabled or cipher suites are inappropriate)
nested: javax.net.ssl.SSLHandshakeException: No appropriate protocol
at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:549)
at lucee.runtime.net.mail.MailClient.start(MailClient.java:296)
at lucee.runtime.tag._Mail.doStartTag(_Mail.java:259)
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol
at sun.security.ssl.HandshakeContext.(HandshakeContext.java:163)

Analysis
MailClient.start() in lucee.core (bundle47) constructs a new Properties() with no parent and never sets mail.imaps.ssl.protocols. This means:

  • The -Dmail.imaps.ssl.protocols JVM system property has no effect (JVM system properties do not flow into a parentless Properties object).
  • Lucee admin mail server config has no field to set this value.

JavaMail’s SocketFetcher.configureSSLSocket() then calls SSLSocket.getEnabledProtocols().
On x86 OpenJDK 21 within the OSGi bundle classloader, this returns [TLSv1.3, TLSv1.2, TLSv1.1, TLSv1] — including protocols that are globally disabled by jdk.tls.disabledAlgorithms.
Passing this list to setEnabledProtocols() followed by startHandshake() causes the JVM to throw No appropriate protocol because TLSv1 and TLSv1.1 are disabled at the security policy level.

When mail.imaps.ssl.protocols=TLSv1.3 TLSv1.2 is present in the session Properties, SocketFetcher instead calls setEnabledProtocols([TLSv1.3, TLSv1.2]) and the handshake succeeds.

Platform note: On ARM Ubuntu 24.04 with the same OpenJDK 21.0.11 and identical jdk.tls.disabledAlgorithms, getEnabledProtocols() correctly returns only [TLSv1.3, TLSv1.2] (disabled protocols are already filtered at the socket level). The bug does not manifest there. This indicates an x86 vs ARM difference in how the JVM applies jdk.tls.disabledAlgorithms within an OSGi classloader context.

Steps to Reproduce

  1. Run Lucee 7.0.4.34 on Java 21 x86 Linux with jdk.tls.disabledAlgorithms including TLSv1, TLSv1.1 (default on Ubuntu 24.04 / Debian Bookworm OpenJDK 21 packages).
  2. Call <cfimap action="getHeaderOnly" server="imap.gmail.com" port="993" secure="true" ...>.
  3. Observe SSLHandshakeException: No appropriate protocol.

Potential Fix

In lucee.runtime.net.mail.MailClient.start(), after constructing the session Properties, add:

if (props.getProperty("mail." + type + ".ssl.protocols") == null) {
    props.setProperty("mail." + type + ".ssl.protocols", "TLSv1.3 TLSv1.2");
}

Where type resolves to imaps for secure IMAP connections. This mirrors what configureSSLSocket() would receive if the property were set, and avoids the JVM attempting to negotiate disabled protocol versions.

Alternatively, expose an sslProtocols attribute on <cfimap> or a corresponding Lucee admin mail server setting so administrators can configure this without modifying application code.

This bug is blocking updating past 7.0.2.106 for several of our major clients.
@Zackster can this be looked at please.

Thanks,
Paul

Are you loading the PDF extension?
See https://dev.lucee.org/t/bundled-pdf-extension-silently-breaks-all-outbound-tls-on-lucee-7-java-21-bouncycastle-1-38-registered-as-a-jvm-security-provider/17356

@PaulHaddon , I tested this on Linux (Docker) with OpenJDK 21 and Lucee 7.0.4.34, but I was not able to reproduce the issue. In my testing, SSLSocket.getEnabledProtocols() returned only TLSv1.3 and TLSv1.2 , and the IMAPS connection succeeded.

Could you share how you verified that SSLSocket.getEnabledProtocols() returns TLSv1.3 , TLSv1.2 , TLSv1.1 , and TLSv1 on the affected system? That would help us understand the difference in behavior.

Can you confirm that this is on AMD64?

Our testing is showing that developers Docker machines on ARM64 are succeeding, unlike our production servers that are on AMD64. Same Java, Tomcat, Lucee JAR.

@PaulHaddon ,
Thanks for the clarification.

My testing was performed on an Intel AMD64/x86_64 system using Linux (Docker/WSL2), Lucee 7.0.4.34, Tomcat 11.0.22, and Eclipse Adoptium OpenJDK 21.0.11. I was not able to reproduce the issue. In my environment, both SSLSocket.getEnabledProtocols() and SSLEngine.getEnabledProtocols() returned only TLSv1.3 and TLSv1.2, and the IMAPS connection completed successfully.

Further to Paul’s work we ran more test via claude-code and have this summary largely supporting the above and noting it is definitely different to the icepdf issue cross referenced in this ticket

@cfmitrah @Zackster — I did a controlled comparison across both architectures and can confirm the fix Paul proposed in the first post works, and can also explain why cfmitrah couldn’t reproduce it.

It’s not an ARM-vs-AMD64 JVM difference. I wrote a minimal reproduction using plain javax.net.ssl.SSLSocketFactory.getDefault() — get an SSLSocket, call getEnabledProtocols(), pass that straight back into setEnabledProtocols(), then startHandshake() against a real IMAPS host. Ran it on both an arm64 box and an x86_64 box, same Lucee 7.0.4.34 / OpenJDK 21.0.11 build on both, identical java.security (jdk.tls.disabledAlgorithms byte-for-byte the same on both). On both architectures, getEnabledProtocols() correctly returned only [TLSv1.3, TLSv1.2] and the handshake succeeded. So plain JSSE default protocol negotiation is fine on x86_64 — that’s consistent with what cfmitrah found, not a contradiction.

The bug only shows up going through JavaMail’s bundled SocketFetcher, not plain JSSE. I bypassed entirely and called com.sun.mail.util.SocketFetcher.getSocket() directly (Lucee’s bundled JavaMail 1.6.7) with a Properties object that does not set mail.imap.ssl.protocols — same pattern MailClient.start() uses. This reproduces SSLHandshakeException: No appropriate protocol deterministically, 100% of the time, on a fresh JVM boot, on x86_64. So SocketFetcher.configureSSLSocket() is doing something different from a default SSLSocketFactory-created socket when building its enabled-protocols list — worth someone with JavaMail 1.6.7 source handy confirming exactly what. If cfmitrah’s repro used a plain JSSE test rather than routing through SocketFetcher/MailClient specifically, that would fully explain the “can’t reproduce” result.

Paul’s proposed fix works — confirmed live. No build environment for Lucee’s source handy, so I patched the compiled MailClient.class directly with ASM (Lucee conveniently bundles ObjectWeb ASM 9.8 itself). Added exactly:

props.setProperty(“mail.” + type + “.ssl.protocols”, “TLSv1.2 TLSv1.3”);

right after type is assigned from getTypeAsString() in start(). Implementation note: getTypeAsString() returns a bare type string (“imap”, “imaps”) — the “mail.” prefix and “.ssl.protocols” suffix are concatenated separately elsewhere (confirmed via the class’s StringConcatFactory bootstrap templates). Paul’s line already has this right — flagging it since it’s easy to get subtly wrong.

Installed the patched class into the running engine and re-ran our actual production call — clean success, real IMAP data returned, zero exceptions. Reverted afterward since a bytecode patch isn’t something we want to maintain ourselves — but it confirms Paul’s proposed fix is correct and sufficient.

Happy to share the patched .class or more repro detail if that helps prioritize a fix. Currently blocking us from staying on a current engine version — any indication of timeline appreciated.

(Re: KabutoTX1’s PDF extension/BouncyCastle question — different bug, confirmed unrelated: a fresh restart with zero icepdf/bouncycastle classes loaded still hits the exact same exception.)