I am using several third party libs which have dependencies to various bouncycastle libraries. Here some examples:
After updating one of these dependencies, I experienced very strange behavior at runtime, not finding methods or fields. It seems that a class of version X has been loaded by the classloader and after that an incompatible class of version Y could not find a field in version X.
So far so good. I tried to fix this using maven dependency management so that compatible versions are used at compile time.
I chose 1.79 for bc...-jdk15to18 and bc...-jdk18on and 2.73.7 for bc...-lts8on, because:
"Release 2.73.7 8 November, 2024 This release is based on BC 1.79 [...]" https://www.bouncycastle.org/download/bouncy-castle-java-lts/
so i thought this is safe, but actually it seems not to be safe. Still NoSuchFieldException, NoSuchMethodException and so on
I checked github mirrors for a Class which made some troubles:
You can see that the sources are different and declaring different public methods, e.g. SHA384Digest#newInstance().
EDIT
Since I answered the title question within the post itself, let me just clarify:
Are bouncycastle java libraries compatible with themselves?
No.
First question: Should this considered to be a bug?
No. In the sense of 'if you file a bug report it would be denied with "works as intended"/"wontfix" because BC isn't designed to operate properly when multiple versions of itself are all on the classpath'.
And that goes for virtually all java libraries. Sticking two different versions of the same library on the classpath breaks stuff. Even if your code seems to run fine right now it's likely not to in some nook or cranny so don't ever do that.
There are a few ways to solve the problem.
Just stick the newest release of BC on the classpath and nothing else. Build tools tend to do this automatically, or if not, have options to 'exclude' older versions.
Now the requirement is that BC as a library is backwards compatible. Generally libraries are intended to be backwards compatible unless their documentation states that they are not (such as google's guava library which explicitly states it is not).
However, be aware of Hyrum's Law:
Any update is potentially capable of being incompatible. Just, usually, especially if the library author intends to be backwards compatible, it'll be fine.
OSGi is the most famous one; various appservers (think 'web server thing that hosts multiple java-written webapps' sometimes also have such functionality.
Each classloader can be 'independent' of all others; you can load BC15 in one of them and BC18 in another, both are loaded simultaneously, and the same class (same name, same package) from 2 different versions can both be loaded. Whatever code you have that needs BC15 uses BC15, whatever uses BC18 uses BC18.
The downsides are threefold:
ImmutableList
(a guava type) as return type or parameter type on any 'exported' method (any method intended to be invoked by other modules).Fatjar schemes will open a dependency (such as BC), take every class file, and rewrite it: It renames the package from org.bouncycastle
to com.foo.yourapp.org.bouncycastle
generally. This is a big mistake in the design of these fatjar schemes; it would have been much smarter to go with e.g. fatjar.c12355124123.org.bouncycastle
where the c thing is a hash, or fatjar.v15.org.bouncycastle
where the v thing is the full version string. That way, actually identical versions are merged and there will never be a conflict. 2 modules can talk to each other in terms of the dependency if they have the exact same version.
Various fatjar tools can be configured to work like that. Or you insulate the BC15-needing stuff and the BC18-needing stuff and fatjar them independently so that the post-fatjar names don't overlap.
fatjarring is annoying: It needlessly messes up your deployments. It takes much longer (unjarring, rewriting, rejarring into single massive jars, then transferring these giants to CI servers, test environments, and production), for no good reason as jars can list their own classpaths. There are also issues with how jars can be 'signed' and fatjar breaks this, and the BC jars are signed.
Hence, this one is not recommended.
Note that if you're already fatjarring or the deps you have are external and badly fatjarred, then you might have to commit to this option.