I have a classloading issue. My application is bundled as a EAR, made of a WAR + some EJB and JAR submodules. The EAR bundles its own version of Bouncy Castle Library, precisely two modules of that library:
- bcprov-jdk18on-1.77.jar
- bcpg-jdk18on-1.77.jar
JBoss EAP 7.4, instead, comes with its own version of Bouncy Castle, which is 1.68 in its "jdk15on" variant. For instance, the corresponding JARs are:
- bcprov-jdk15on-1.68.0.redhat-00005.jar
- bcpg-jdk15on-1.68.0.redhat-00005.jar
The problem I observe is that a mixture of classes from the two versions are used, and this leads to runtime errors like NoSuchMethodErrors. Here is a typical stack trace excerpt:
Caused by: java.lang.NoSuchMethodError: org.bouncycastle.crypto.engines.AESEngine.newInstance()Lorg/bouncycastle/crypto/MultiBlockCipher;
at org.bouncycastle.openpgp.operator.bc.BcImplProvider.createBlockCipher(Unknown Source)
at org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder$1.recoverKeyData(Unknown Source)
at org.bouncycastle.openpgp.PGPSecretKey.extractKeyData(Unknown Source)
at org.bouncycastle.openpgp.PGPSecretKey.extractPrivateKey(Unknown Source)
I have enabled the classloader verbose logging in JBoss and this is what I see:
2024-02-15 14:22:48,230 TRACE [org.jboss.modules] (Weld Thread Pool -- 1) Finding class org.bouncycastle.openpgp.PGPSecretKey from Module "deployment.myapp-ear.ear.myapp-ejb.jar" from Service Module Loader
2024-02-15 14:22:48,230 TRACE [org.jboss.modules] (Weld Thread Pool -- 1) Finding local class org.bouncycastle.openpgp.PGPSecretKey from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:22:48,230 TRACE [org.jboss.modules] (Weld Thread Pool -- 1) Loading class org.bouncycastle.openpgp.PGPSecretKey locally from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Finding class org.bouncycastle.openpgp.operator.bc.BcImplProvider from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Finding local class org.bouncycastle.openpgp.operator.bc.BcImplProvider from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Loading class org.bouncycastle.openpgp.operator.bc.BcImplProvider locally from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Attempting to define class org.bouncycastle.openpgp.operator.bc.BcImplProvider in Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules.define] (default task-35) Defined class org.bouncycastle.openpgp.operator.bc.BcImplProvider in Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Finding local class org.bouncycastle.crypto.BlockCipher from Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,162 TRACE [org.jboss.modules] (default task-35) Loading class org.bouncycastle.crypto.BlockCipher locally from Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,163 TRACE [org.jboss.modules] (default task-35) Attempting to define class org.bouncycastle.crypto.BlockCipher in Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,163 TRACE [org.jboss.modules.define] (default task-35) Defined class org.bouncycastle.crypto.BlockCipher in Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,563 TRACE [org.jboss.modules] (default task-35) Finding class org.bouncycastle.crypto.engines.AESEngine from Module "deployment.myapp-ear.ear" from Service Module Loader
2024-02-15 14:27:33,563 TRACE [org.jboss.modules] (default task-35) Finding local class org.bouncycastle.crypto.engines.AESEngine from Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,564 TRACE [org.jboss.modules] (default task-35) Loading class org.bouncycastle.crypto.engines.AESEngine locally from Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
2024-02-15 14:27:33,564 TRACE [org.jboss.modules] (default task-35) Attempting to define class org.bouncycastle.crypto.engines.AESEngine in Module "org.bouncycastle.bcprov" version 1.68.0.redhat-00005 from local module loader @4a11eb84 (finder: local module finder @4e858e0a (roots: /opt/rh/eap7/root/usr/share/wildfly/modules,/opt/rh/eap7/root/usr/share/wildfly/modules/system/layers/base))
So, as you can see:
PGPSecretKeyfrom bcpg module is loaded from my own version of Bouncy Castle (1.77), bundled in the EARBcImplProviderfrom bcprov module is still loaded from my own version of Bouncy Castle (1.77), bundled in the EARBlockCipherandAESEnginefrom bcprov module are loaded from JBoss's version of Bouncy Castle (1.68)
I suspect that some code from JBoss itself is using Bouncy Castle 1.68 and this is causing the JBoss classloader to reuse classes from it whenever they are already in memory. But what I would like to achieve, instead, is a complete isolation (at least for this library) between the classes loaded to run JBoss and the ones loaded to run my application. Or at least a "child-first" approach when searching for a class which was not yet requested by my own application.
Please note that:
- the EAR is built with Maven; the Bouncy Castle dependencies are compile dependencies, not provided ones; the BC 1.77 JARs are correctly bundled in the EAR
- I already tried to put
org.bouncycastle.bcprovandorg.bouncycastle.bcpgamong the excluded dependencies injboss-deployment-structure.xml, with no luck; but I don't expect it to be needed anyway, since Bouncy Castle is not listed among the implicit module dependencies and seems to be a private module - following one suggestion given on another StackOverflow answer, I tried to add the two JARs as resource roots with
use-physical-code-sourceset totrue, once again injboss-deployment-structure.xml, with no luck - please do not suggest to put the newer Bouncy Castle version on JBoss EAP's classpath: I'd like my application to use its bundled version without touching the server configuration, especially because other applications may still benefit from using the bundled (and older) Bouncy Castle version provided by JBoss EAP
The solution in my case was to add an exclusion for the whole Bouncy Castle module, not just for the Bouncy Castle submodules I am using (
org.bouncycastle.bcprovandorg.bouncycastle.bcpg):So in
META-INF/jboss-deployment-structure.xml:With
<ear-exclusions-cascaded-to-subdeployments>set totrueI also ensure the exclusion is applied to all submodules.I guess this might bue due to the fact that in our JBoss EAP installation the Bouncy Castle module as a whole seems to be made available to all deployed applications.