You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含Bouncy Castle依赖的项目打包单Jar适配Oracle JRE问题求助

解决Bouncy Castle签名与Oracle JRE单Jar打包冲突问题

问题背景

项目依赖自研模块,而自研模块依赖带签名的Bouncy Castle(BC)库,打包时陷入两难:

  • 若将BC的签名文件(.SF/.DSA/.RSA)包含在shaded Jar中,JVM会判定签名无效,报错如下:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
    at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:340)
    at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:282)
    at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:320)
    at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:232)
    at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:760)
    at java.base/java.util.jar.JarFile.getInputStream(JarFile.java:856)
    at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:848)
    at java.base/jdk.internal.loader.Resource.cachedInputStream(Resource.java:77)
    at java.base/jdk.internal.loader.Resource.getByteBuffer(Resource.java:163)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:853)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:534)
    at java.base/java.lang.Class.forName(Class.java:513)
    at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:797)
    at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:692)
  • 若排除签名文件,OpenJDK JRE可正常运行,但Oracle JRE要求JCE安全提供商必须签名,触发报错:
java.lang.SecurityException: JCE cannot authenticate the provider BC
    at java.base/javax.crypto.JceSecurity.getInstance(JceSecurity.java:150)
    at java.base/javax.crypto.KeyAgreement.getInstance(KeyAgreement.java:280)
    at ... (my dependency's classes)
Caused by: java.util.jar.JarException: The JCE Provider file:/app/simple-usecase-0.0.6.jar is not signed.
    at javax.crypto.JarVerifier.verifySingleJar(JarVerifier.java:440) ~[?:?]
    at javax.crypto.JarVerifier.verifyJars(JarVerifier.java:319) ~[?:?]
    at javax.crypto.JarVerifier.verify(JarVerifier.java:263) ~[?:?]
    at javax.crypto.ProviderVerifier.verify(ProviderVerifier.java:130) ~[?:?]
    at javax.crypto.JceSecurity.verifyProvider(JceSecurity.java:196) ~[?:?]
    at javax.crypto.JceSecurity$2.apply(JceSecurity.java:222) ~[?:?]
    at javax.crypto.JceSecurity$2.apply(JceSecurity.java:211) ~[?:?]
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[?:?]
    at javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:211) ~[?:?]
    at javax.crypto.JceSecurity.getInstance(JceSecurity.java:146) ~[?:?]
    at javax.crypto.KeyAgreement.getInstance(KeyAgreement.java:280) ~[?:?]
    at ... (my dependency's classes again)

目标运行环境:

java 21.0.3 2024-04-16 LTS
Java(TM) SE Runtime Environment (build 21.0.3+7-LTS-152)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.3+7-LTS-152, mixed mode, sharing)

已尝试的方案

  1. Maven Shade插件排除所有签名文件:配置过滤所有依赖的签名文件,OpenJDK正常但Oracle JRE失败,配置如下:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <minimizeJar>false</minimizeJar>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <dependencyReducedPomLocation>
                    ${java.io.tmpdir}/dependency-reduced-pom.xml
                </dependencyReducedPomLocation>
                <relocations>
                    <relocation>
                        <pattern>com.acme.coyote</pattern>
                        <shadedPattern>hidden.coyote</shadedPattern>
                    </relocation>
                </relocations>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>Main</mainClass>
                    </transformer>
                </transformers>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
            </configuration>
        </execution>
    </executions>
</plugin>
  1. Maven Assembly插件打包:使用jar-with-dependencies descriptor,同样OpenJDK正常、Oracle JRE失败。
  2. 排除BC依赖手动添加类路径:在Shade配置中排除所有BC依赖,运行时用-cp指定BC Jar,但类加载器无法找到BC类(OpenJDK也失败)。

项目使用OpenJDK 21编译,目标版本21,BC依赖:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.78.1</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcutil-jdk18on</artifactId>
    <version>1.78.1</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk18on</artifactId>
    <version>1.78.1</version>
</dependency>

可行解决方案

方案1:精准过滤签名文件,仅保留BC的签名

修改Shade插件的filter配置,只排除非BC依赖的签名文件,保留BC自身的签名:

<filters>
    <!-- 排除所有非BC依赖的签名文件 -->
    <filter>
        <artifact>*:*</artifact>
        <excludes>
            <exclude>META-INF/*.SF</exclude>
            <exclude>META-INF/*.DSA</exclude>
            <exclude>META-INF/*.RSA</exclude>
        </excludes>
        <includes>
            <include>org/bouncycastle/**</include>
        </includes>
    </filter>
    <!-- 保留BC依赖的签名文件 -->
    <filter>
        <artifact>org.bouncycastle:*</artifact>
        <includes>
            <include>META-INF/*.SF</include>
            <include>META-INF/*.DSA</include>
            <include>META-INF/*.RSA</include>
        </includes>
    </filter>
</filters>

这样既不会因为混合签名导致JVM判定无效,又能保留BC的签名满足Oracle JRE的JCE验证要求。

方案2:将BC作为外部依赖打包,通过Manifest指定Class-Path

使用Shade插件将BC Jar打包到最终Jar的lib目录,并在Manifest中添加Class-Path指向这些Jar:

  1. 配置Shade插件排除BC依赖并生成正确的Manifest:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <minimizeJar>false</minimizeJar>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <relocations>
                    <relocation>
                        <pattern>com.acme.coyote</pattern>
                        <shadedPattern>hidden.coyote</shadedPattern>
                    </relocation>
                </relocations>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>Main</mainClass>
                        <manifestEntries>
                            <Class-Path>lib/bcprov-jdk18on-1.78.1.jar lib/bcutil-jdk18on-1.78.1.jar lib/bcpkix-jdk18on-1.78.1.jar</Class-Path>
                        </manifestEntries>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                </transformers>
                <artifactSet>
                    <excludes>
                        <exclude>org.bouncycastle:*</exclude>
                    </excludes>
                </artifactSet>
            </configuration>
        </execution>
    </executions>
</plugin>
  1. 添加maven-dependency-plugin复制BC Jar到lib目录:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.6.1</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                <includeGroupIds>org.bouncycastle</includeGroupIds>
            </configuration>
        </execution>
    </executions>
</plugin>

打包后,将最终Jar和lib目录放在同一目录下运行,Oracle JRE会加载原始签名的BC Jar,满足验证要求。

方案3:对最终Shaded Jar重新签名

若有合法的代码签名证书,可以对排除了第三方签名后的Shaded Jar进行重新签名:

  1. 先使用Shade插件排除所有签名文件(如之前的配置)
  2. 使用jarsigner工具对最终Jar签名:
jarsigner -keystore your-keystore.jks -storepass your-password your-shaded.jar your-alias

签名后的Jar会被Oracle JRE认可为合法的JCE提供商容器。

内容的提问来源于stack exchange,提问作者Felipe Rossi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:54:53