Skip to content

bctls-jdk18on:1.86 multi-release JAR is inconsistent: NoSuchMethodError on SSLEngineUtil.create(ContextData) on JDK 9+ #2448

Description

@jmartisk

Summary

bctls-jdk18on version 1.86 ships a broken multi-release JAR. On any JDK 9 or
newer, creating an SSLEngine from the BCJSSE provider fails with:

java.lang.NoSuchMethodError: 'javax.net.ssl.SSLEngine
  org.bouncycastle.jsse.provider.SSLEngineUtil.create(org.bouncycastle.jsse.provider.ContextData)'
    at org.bouncycastle.jsse.provider.ProvSSLContextSpi.engineCreateSSLEngine(Unknown Source)
    at java.base/javax.net.ssl.SSLContext.createSSLEngine(SSLContext.java:373)

The regression is a packaging problem, not a source problem: the versioned
(META-INF/versions/9/) copy of ProvSSLContextSpi that existed in 1.85 is
missing from the 1.86 JAR, while the versioned copy of SSLEngineUtil
(whose create(...) return type differs from the base copy) is still present.
That leaves the base ProvSSLContextSpi linked against a method signature that
does not exist in the versioned SSLEngineUtil the JVM actually loads.

  • Affected artifact: org.bouncycastle:bctls-jdk18on:1.86
  • Last working version: org.bouncycastle:bctls-jdk18on:1.85
  • Impact: BCJSSE is unusable on JDK 9+ (SSLContext.createSSLEngine() throws)

Environment

  • JDK: Temurin 21.0.9+10 (reproduces on any JDK ≥ 9)
  • OS: Linux
  • Artifacts: bcprov-jdk18on:1.86, bcutil-jdk18on:1.86, bctls-jdk18on:1.86

Minimal reproducer

No third-party framework required — just the three BC 1.86 JARs on the classpath:

import java.security.*;
import javax.net.ssl.*;

public class Repro {
    public static void main(String[] a) throws Exception {
        Security.addProvider((Provider) Class
            .forName("org.bouncycastle.jce.provider.BouncyCastleProvider")
            .getDeclaredConstructor().newInstance());
        Security.addProvider((Provider) Class
            .forName("org.bouncycastle.jsse.provider.BouncyCastleJsseProvider")
            .getDeclaredConstructor().newInstance());

        SSLContext ctx = SSLContext.getInstance("TLS", "BCJSSE");
        ctx.init(null, null, null);
        ctx.createSSLEngine();   // <-- throws NoSuchMethodError on JDK 9+
        System.out.println("OK");
    }
}

Run:

javac -cp bcprov-jdk18on-1.86.jar:bctls-jdk18on-1.86.jar:bcutil-jdk18on-1.86.jar Repro.java
java  -cp .:bcprov-jdk18on-1.86.jar:bctls-jdk18on-1.86.jar:bcutil-jdk18on-1.86.jar Repro

1.86 throws NoSuchMethodError; the same program with 1.85 prints OK.

Root cause analysis

bctls-jdk18on is a multi-release JAR (Multi-Release: true). On JDK ≥ 9 the
JVM prefers class files under META-INF/versions/9/ over the base copies.

Two relevant classes:

1. SSLEngineUtil

The base and versioned copies declare a different return type for
create(ContextData):

# base  org/bouncycastle/jsse/provider/SSLEngineUtil.class
static javax.net.ssl.SSLEngine                      create(org.bouncycastle.jsse.provider.ContextData);

# META-INF/versions/9/org/bouncycastle/jsse/provider/SSLEngineUtil.class
static org.bouncycastle.jsse.provider.ProvSSLEngine create(org.bouncycastle.jsse.provider.ContextData);

(This return-type difference exists in 1.85 too and is fine on its own —
ProvSSLEngine is a subtype of SSLEngine. The problem is the caller below.)

2. ProvSSLContextSpi

Its engineCreateSSLEngine() invokes SSLEngineUtil.create(ContextData).
Because invokestatic is resolved by name and full descriptor including the
return type
, the caller's descriptor must match the callee that the JVM loads.

# base ProvSSLContextSpi.engineCreateSSLEngine() bytecode
invokestatic SSLEngineUtil.create:(Lorg/.../ContextData;)Ljavax/net/ssl/SSLEngine;

The base ProvSSLContextSpi is compiled against the base SSLEngineUtil
(return SSLEngine). It must therefore be paired with the base SSLEngineUtil,
or shipped with a matching versioned copy compiled against the versioned
SSLEngineUtil (return ProvSSLEngine).

The packaging regression

Entry 1.85 (works) 1.86 (broken)
META-INF/versions/9/.../SSLEngineUtil.class present present
META-INF/versions/9/.../ProvSSLContextSpi.class present (24239 bytes) MISSING
total META-INF/versions/**/*.class entries 452 23

In 1.85, the JVM loads both versioned copies, which are compiled against
each other — consistent, links fine.

In 1.86, the versioned ProvSSLContextSpi was dropped, so the JVM loads:

  • versioned SSLEngineUtilcreate(ContextData) returns ProvSSLEngine
  • base ProvSSLContextSpi → calls create(ContextData) expecting return SSLEngine

No method with descriptor (ContextData)Ljavax/net/ssl/SSLEngine; exists in the
loaded SSLEngineUtilNoSuchMethodError.

The overall count of versioned classes collapsed from 452 to 23 between the two
releases, which suggests the multi-release layer was regenerated/pruned and a
number of META-INF/versions/9/... classes (including ProvSSLContextSpi) were
unintentionally omitted.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions