🔴 The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOG// DOC-DERIVED — canonical BouncyCastle paste; class/package vary by library: Exception in thread "main" java.lang.SecurityException: class "org.bouncycastle.jce.provider.BouncyCastleProvider"'s signer information does not match signer information of other classes in the same package at java.base/java.lang.ClassLoader.checkCerts(ClassLoader.java:1148) at java.base/java.lang.ClassLoader.defineClass2(Native Method) at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:128)

⚡ Quick Fix Works 80% of the time

Keep exactly one version of the signed library on the classpath — exclude every duplicate copy so one consistent set of signatures defines each package.

mvn -q dependency:tree -Dincludes=org.bouncycastle <exclusions> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <!-- stale copy --> </exclusion> </exclusions>

🧠 Why this Happens

Tap to expand the deep technical explanation

When the first class of a package loads, its code-signing certificates become that package’s registered signers within the classloader. Every subsequent class in the SAME package must present matching signatures; a class arriving with no signature or different certs means someone could have substituted code into a trusted namespace — exactly what the mechanism exists to prevent — so defineClass throws SecurityException instead of linking. Mixing bcprov 1.60 (signed by BC) with a shaded or rebuilt copy of one class from the same package guarantees the mismatch.

The HITEC City Parking Spot Analogy:

An office issues photo badges with holograms. One morning someone walks in wearing the same lanyard but a hand-drawn badge — security does not debate quality; anyone without the exact hologram is turned away for the whole floor’s safety.

🔁 How to Reproduce Confirm this is your error

Place two bouncycastle jars of different versions on the classpath (or re-sign one class of a signed package yourself) and load provider classes from both. DOC-DERIVED — wording frozen for decades.

🛠️ Solutions (5 Ways to Fix)

Solution 1✓ Most common cause

Deduplicate to a single version of the signed library

👉 Use this first — mixed versions inside one package are nearly always the cause.

dependency:tree lists every path to BouncyCastle artifacts; exclude all but your chosen release so one certificate set owns the whole package.

mvn -q dependency:tree -Dincludes=org.bouncycastle # keep ONE of bcprov-jdk18on/bcprov-jdk15on, exclude siblings and old transitives
Solution 2

Shade correctly: relocate packages AND strip signatures as a unit

👉 Use this when you must embed the library in a fat jar.

Partial META-INF stripping leaves some classes signed and others anonymous — the mismatch itself. Either keep the jar unshaded as an external dependency, or relocate the entire package to your namespace while removing ALL original signature files consistently.

<relocations> <relocation> <pattern>org.bouncycastle</pattern> <shadedPattern>shaded.bc</shadedPattern> </relocation> </relocations> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters>
Solution 3

Purge stray copies from server lib/endorsed directories

👉 Use this when the build tree is clean but servers still misbehave.

Tomcat lib/, WebSphere endorsed dirs, and old exploded wars often hold a second signed copy from years past. The runtime merges those with yours inside one loader — sweep them.

find $CATALINA_HOME -name "*bcprov*.jar" -o -name "*bcpkix*.jar" # delete everything except the single version you standardize on
Solution 4

Verify what you deploy with jarsigner

👉 Use this during diagnosis to see the actual certificate sets per jar.

jarsigner -verify -verbose prints each entry’s signing status; comparing outputs across your jars reveals whose entries are unsigned or differently certified before you ever run the app.

jarsigner -verify -verbose:summary bcprov-jdk18on-1.78.jar | head jarsigner -verify target/app.jar | grep -i unsigned
Solution 5

Disable signature checking via custom classloader

👉 DEV ONLY — never ship this; see below.

DEV ONLY. A classloader that overrides secure definition drops the integrity guarantee the error protects: any tampered class in a trusted package would now load silently. Useful only for a local experiment proving the diagnosis; production must fix packaging instead.

// DEV ONLY - diagnostic harness only new ClassLoader(parent) { @Override protected Class<?> loadClass(String name, boolean resolve) { // skip checkCerts path by defining bytes directly return findClass(name); // simplified, insecure } };

📋 Version Notes

Java 8

Package-signer consistency enforced identically since Java 1.2.

Java 11

Unchanged; shaded fat jars increasingly common trigger.

Java 17

Same check; message unchanged.

Java 21

Unchanged.

🛡️ How to Prevent This Next Time

Ban duplicate packages across artifacts in CI, treat signed libraries as unshadable without full relocation, and record expected signing certificates for security-critical dependencies in the supply-chain checklist.