运行Jar包时加载.pem私钥报错,IDEA中运行正常
问题描述
- 环境:Java 17、Maven 3.9.1、Spring Boot 3.0.4
- 需求:加载.pem格式加密私钥,解密配对加密数据
- 现象:IDEA内运行正常,但通过
mvn clean install/package生成的Jar包执行时,加载私钥环节报错
加载私钥代码
public PrivateKey loadPrivateKeyFromFile(String privateKeyPath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, OperatorCreationException, PKCSException { try (FileReader fileReader = new FileReader(privateKeyPath); PEMParser pemParser = new PEMParser(fileReader)) { Object pemObject = pemParser.readObject(); if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) { PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemObject; InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build("pass".toCharArray()); /* 试过把这段代码放在多个位置,这是最后一次尝试的位置 */ Security.addProvider(new BouncyCastleProvider()); ASN1OctetString asn1OctetString= encryptedPrivateKeyInfo.decryptPrivateKeyInfo (decryptorProvider).getPrivateKey(); byte[] privateKeyBytes = asn1OctetString.getOctets(); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } else { throw new IllegalArgumentException("Unsupported PEM object: " + pemObject); } } }
错误位置
报错代码行:
ASN1OctetString asn1OctetString= encryptedPrivateKeyInfo.decryptPrivateKeyInfo (decryptorProvider).getPrivateKey();
异常信息
org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: 1.2.840.113549.1.5.13 not available: Cannot find any provider supporting 1.2.840.113549.3.7
已尝试方法
- 添加BouncyCastle安全提供者(确认BC库存在)
- 验证BC库已打包进Jar
- 使用maven-shade-plugin打包
- 尝试IDEA直接生成Jar包
POM.xml配置
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.micro</groupId> <artifactId>encrypt</artifactId> <version>0.0.1-SNAPSHOT</version> <name>encryption</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.santuario</groupId> <artifactId>xmlsec</artifactId> <version>2.1.6</version> </dependency> <dependency> <groupId>javax.xml.stream</groupId> <artifactId>stax-api</artifactId> <version>1.0-2</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.70</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>io.projectreactor.netty</groupId> <artifactId>reactor-netty</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <shadeTestJar>true</shadeTestJar> <shadedClassifierName>SHADED</shadedClassifierName> <shadedArtifactAttached>true</shadedArtifactAttached> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
解决思路
1. 提前注册BouncyCastle并设置最高优先级
当前代码在创建解密器后才注册BC提供者,时机太晚。需在应用启动最开始注册,确保JDK优先使用BC的算法实现:
// 放在启动类main方法最开头 Security.insertProviderAt(new BouncyCastleProvider(), 1);
2. 升级BouncyCastle版本适配Java 17
1.70版本的BC对Java 17兼容性不足,建议升级到稳定版(如1.77+),同时更换为Java 17适配的jdk18on后缀依赖:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.77</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> <version>1.77</version> </dependency>
3. 修复maven-shade-plugin配置,避免SPI文件丢失
shade插件默认会覆盖META-INF/services目录下的SPI文件,导致BC算法无法被JDK识别。需添加转换器合并这些文件:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <shadeTestJar>true</shadeTestJar> <shadedClassifierName>SHADED</shadedClassifierName> <shadedArtifactAttached>true</shadedArtifactAttached> <transformers> <!-- 合并SPI服务文件 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <!-- 指定Spring Boot主类 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.micro.encrypt.EncryptionApplication</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>
也可以直接移除shade插件,使用Spring Boot官方maven插件打包,它会自动处理依赖合并问题。
4. 明确指定KeyFactory使用BC提供者
创建KeyFactory时,显式指定BC作为提供者,避免JDK默认找不到算法:
KeyFactory keyFactory = KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
内容的提问来源于stack exchange,提问作者NigDra
相关产品推荐
相关产品推荐

