如何在JBoss EAP 7.4部署的应用中使用自定义Bouncy Castle版本
问题背景
应用打包为EAR(包含WAR、EJB及JAR子模块),内置Bouncy Castle 1.77版本的bcprov-jdk18on-1.77.jar和bcpg-jdk18on-1.77.jar;JBoss EAP 7.4自带1.68版本的对应JAR。版本混合加载触发NoSuchMethodError,栈跟踪如下:
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)
类加载日志确认:
- bcpg模块的
PGPSecretKey、bcprov模块的BcImplProvider来自应用内置1.77版本 - bcprov模块的
BlockCipher和AESEngine来自JBoss自带1.68版本
推测JBoss自身代码先加载了1.68版本类,导致类加载器复用该版本。需要实现类隔离或子优先加载,且满足:
- Maven构建,BC依赖为compile范围,已正确打包进EAR
- 已尝试常规
jboss-deployment-structure.xml排除模块、设置资源根无效 - 不修改JBoss服务器配置、不替换自带BC版本
解决方案
1. 修正EAR级jboss-deployment-structure.xml配置
常规排除模块无效,是因为子模块(WAR/EJB)可能未继承排除规则,需要在EAR的META-INF/jboss-deployment-structure.xml中统一配置所有子模块,强制加载应用内置JAR:
<?xml version="1.0" encoding="UTF-8"?> <jboss-deployment-structure> <!-- 主EAR部署配置 --> <deployment> <exclude-subsystems> <subsystem name="org.bouncycastle"/> </exclude-subsystems> <exclusions> <module name="org.bouncycastle.bcprov"/> <module name="org.bouncycastle.bcpg"/> </exclusions> <!-- 指定应用内置BC JAR为资源根,强制物理源码加载 --> <resources> <resource-root path="lib/bcprov-jdk18on-1.77.jar" use-physical-code-source="true"/> <resource-root path="lib/bcpg-jdk18on-1.77.jar" use-physical-code-source="true"/> </resources> </deployment> <!-- WAR子模块单独配置,确保继承排除规则并依赖EAR的资源 --> <sub-deployment name="your-web-module.war"> <exclusions> <module name="org.bouncycastle.bcprov"/> <module name="org.bouncycastle.bcpg"/> </exclusions> <dependencies> <module name="deployment.your-ear-name.ear" export="true"/> </dependencies> </sub-deployment> <!-- EJB子模块同理 --> <sub-deployment name="your-ejb-module.jar"> <exclusions> <module name="org.bouncycastle.bcprov"/> <module name="org.bouncycastle.bcpg"/> </exclusions> <dependencies> <module name="deployment.your-ear-name.ear" export="true"/> </dependencies> </sub-deployment> </jboss-deployment-structure>
注意替换your-web-module.war、your-ejb-module.jar、your-ear-name.ear为实际模块名和EAR文件名。
2. 确保Maven打包时BC JAR位置正确
在EAR的pom.xml中,通过maven-ear-plugin将BC JAR打包到EAR的lib目录(对应上述配置的path="lib/..."):
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ear-plugin</artifactId> <version>3.3.0</version> <configuration> <defaultLibBundleDir>lib</defaultLibBundleDir> <modules> <webModule> <groupId>your.group.id</groupId> <artifactId>your-web-module</artifactId> <contextRoot>/your-app</contextRoot> </webModule> <ejbModule> <groupId>your.group.id</groupId> <artifactId>your-ejb-module</artifactId> </ejbModule> </modules> </configuration> </plugin> </plugins> </build>
3. 备选:EAR内部自定义模块隔离
如果上述配置仍无效,可在EAR内部创建自定义模块完全隔离BC版本:
- 在EAR的
META-INF/modules/org/bouncycastle/main目录下放入bcprov-jdk18on-1.77.jar、bcpg-jdk18on-1.77.jar,并添加module.xml:
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.3" name="org.bouncycastle"> <resources> <resource-root path="bcprov-jdk18on-1.77.jar"/> <resource-root path="bcpg-jdk18on-1.77.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.security.auth.message.api"/> </dependencies> </module>
- 修改
jboss-deployment-structure.xml让应用依赖这个自定义模块:
<?xml version="1.0" encoding="UTF-8"?> <jboss-deployment-structure> <deployment> <dependencies> <module name="org.bouncycastle" slot="main" export="true" /> </dependencies> <exclusions> <module name="org.bouncycastle.bcprov"/> <module name="org.bouncycastle.bcpg"/> </exclusions> </deployment> </jboss-deployment-structure>
4. 验证效果
部署后,通过JBoss CLI开启类加载日志验证:
/subsystem=logging/logger=org.jboss.modules:write-attribute(name=level,value=DEBUG)
触发业务场景后,检查日志中所有org.bouncycastle开头的类加载来源,确认均来自应用内置的1.77版本JAR。
内容的提问来源于stack exchange,提问作者Mauro Molinari

