Maven项目运行时抛出java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider异常
我是Java和BouncyCastle的新手,想搭建一个简单的Java应用用BouncyCastle生成CSR,我按照以下步骤操作:
- 用下面的命令创建Maven项目:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-bouncycastle-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
- 给pom.xml添加BouncyCastle依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-bouncycastle-app</artifactId> <version>1.0-SNAPSHOT</version> <name>my-bouncycastle-app</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.release>17</maven.compiler.release> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.11.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> <version>1.79</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.79</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> <!-- Optionally: parameterized tests support --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.4.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.0</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.4.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>3.1.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>3.1.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.12.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.6.1</version> </plugin> </plugins> </pluginManagement> </build> </project>
- 修改主类代码:
package com.mycompany.app; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.Security; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * Hello world! */ public class App { public static void main(String[] args) { System.out.println("Start my BouncyCastle App"); Security.setProperty("crypto.policy", "unlimited"); Security.addProvider(new BouncyCastleProvider()); KeyPairGenerator keyGen; try { keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair keyPair = keyGen.generateKeyPair(); System.out.println("Public Key: " + keyPair.getPublic()); System.out.println("Private Key: " + keyPair.getPrivate()); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); System.exit(-1); } } }
- 用
mvn clean package命令打包,打包成功:
[INFO] Scanning for projects... [INFO] [INFO] ---------------< com.mycompany.app:my-bouncycastle-app >---------------- [INFO] Building my-bouncycastle-app 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- clean:3.4.0:clean (default-clean) @ my-bouncycastle-app --- [INFO] Deleting /Users/my-name/Java/Maven/my-bouncycastle-app/target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ my-bouncycastle-app --- [INFO] skip non existing resourceDirectory /Users/my-name/Java/Maven/my-bouncycastle-app/src/main/resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ my-bouncycastle-app --- [INFO] Recompiling the module because of changed source code. [INFO] Compiling 1 source file with javac [debug release 17] to target/classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ my-bouncycastle-app --- [INFO] skip non existing resourceDirectory /Users/my-name/Java/Maven/my-bouncycastle-app/src/test/resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ my-bouncycastle-app --- [INFO] Recompiling the module because of changed dependency. [INFO] Compiling 1 source file with javac [debug release 17] to target/test-classes [INFO] [INFO] --- surefire:3.3.0:test (default-test) @ my-bouncycastle-app --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.mycompany.app.AppTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 s -- in com.mycompany.app.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- jar:3.4.2:jar (default-jar) @ my-bouncycastle-app --- [INFO] Building jar: /Users/my-name/Java/Maven/my-bouncycastle-app/target/my-bouncycastle-app-1.0-SNAPSHOT.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.576 s [INFO] Finished at: 2024-11-20T14:58:58-06:00 [INFO] ------------------------------------------------------------------------
- 运行生成的jar文件时出错:
% java -cp target/my-bouncycastle-app-1.0-SNAPSHOT.jar com.mycompany.app.App Error: Unable to initialize main class com.mycompany.app.App Caused by: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
我尝试手动把BouncyCastle的jar加到classpath里,但还是不行:
% java -cp target/my-bouncycastle-app-1.0-SNAPSHOT.jar:~/.m2/repository/org/bouncycastle/bcprov-jdk18on/1.79/bcprov-jdk18on-1.79.jar com.mycompany.app.App Error: Unable to initialize main class com.mycompany.app.App Caused by: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
现在不知道哪里出问题了,求帮忙解决!
问题原因&解决方案
别着急,这个问题其实很常见,主要是因为你打包出来的jar只包含了自己的代码,没有把依赖的BouncyCastle相关jar包加进去,而且你手动加classpath的时候还有两个小问题:
方案1:正确设置完整的classpath(适合快速测试)
你只加了bcprov-jdk18on的jar,但你的项目还依赖了bcpkix-jdk18on;另外在Unix/Linux/macOS下,~在classpath里不会被shell自动解析成用户目录,得换成绝对路径或者用$HOME。
试试这个命令(把/Users/my-name换成你自己的用户目录):
java -cp target/my-bouncycastle-app-1.0-SNAPSHOT.jar:/Users/my-name/.m2/repository/org/bouncycastle/bcprov-jdk18on/1.79/bcprov-jdk18on-1.79.jar:/Users/my-name/.m2/repository/org/bouncycastle/bcpkix-jdk18on/1.79/bcpkix-jdk18on-1.79.jar com.mycompany.app.App
或者用$HOME代替绝对路径:
java -cp target/my-bouncycastle-app-1.0-SNAPSHOT.jar:$HOME/.m2/repository/org/bouncycastle/bcprov-jdk18on/1.79/bcprov-jdk18on-1.79.jar:$HOME/.m2/repository/org/bouncycastle/bcpkix-jdk18on/1.79/bcpkix-jdk18on-1.79.jar com.mycompany.app.App
方案2:用Maven Exec插件直接运行(更省心)
不用自己手动拼classpath,直接用Maven的exec插件来运行你的主类,它会自动帮你把所有依赖都加到classpath里。
首先在pom.xml的<build>标签里的<plugins>(注意是<pluginManagement>外面的<plugins>,如果没有就新建一个)添加exec插件:
<build> <!-- 原来的pluginManagement保留不变 --> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>com.mycompany.app.App</mainClass> </configuration> </plugin> </plugins> </build>
然后直接运行命令:
mvn exec:java
这样就能直接启动你的应用,不用管classpath的问题啦。
方案3:把依赖复制到本地目录再运行(适合部署)
如果不想每次都用Maven运行,可以用maven-dependency-plugin把所有依赖复制到target目录下的lib文件夹里,然后再运行:
在pom.xml的<build>里添加这个插件:
<build> <!-- 原来的pluginManagement和其他插件保留 --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build>
然后执行mvn clean package,这时候target目录下会多一个lib文件夹,里面包含所有依赖的jar。之后运行命令:
java -cp target/my-bouncycastle-app-1.0-SNAPSHOT.jar:target/lib/* com.mycompany.app.App
(Windows下把冒号换成分号;)
这样就不用手动写每个依赖的路径了,用通配符就能加载所有lib里的jar。
备注:内容来源于stack exchange,提问作者feplooptest

