Spring Boot读取trustStore出错

文章讲述了在SpringBoot应用中遇到读取trustStore文件时出现的错误,包括IllegalStateException和NoSuchAlgorithmException等。问题的根本原因是程序错误地将默认的cacerts文件用作信任存储,而不是预期的test.store。通过使用ClassPathResource加载文件,获取绝对路径,并设置系统属性来指定正确的trustStore和密码,成功解决了问题。
部署运行你感兴趣的模型镜像

小结

Spring Boot读取trustStore报错,进行了排查并解决。

问题

读取公钥证书并添加到trustStore中,trustStore名称是test.store,这里在Spring Boot中使用程序访问,报以下错:

...
Caused by: java.lang.IllegalStateException: could not create the default ssl context
...
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)
...
Caused by: java.security.KeyStoreException: problem accessing trust store
...
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
...
Caused by: java.security.UnrecoverableKeyException: Password verification failed
...

解决

首先想到的是密码设置有误,使用以下指令进行排查,密码没问题,可以正常读取:

keytool -list -v -keystore test.store
Enter keystore password:666666
...
...

经过跟踪调试,发现是trustStore文件test.store没有被正确读取。读取到的文件是:C:\Program Files\Java\jdk-11.0.16.1\lib\security\cacerts并被设置到 javax.net.ssl.trustStore。这里test.store这个文件是放在resource下的,修改后使用以下方法可以正常正确读取,问题解决。

		String storePath = null;
		File resource = null;
		try {
			resource = new ClassPathResource(
					"test.store").getFile();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

		storePath = resource.getAbsolutePath();

		System.setProperty("javax.net.ssl.trustStore",
				storePath);
		// 设置trustStore的读取密码
		System.setProperty("javax.net.ssl.trustStorePassword", "666666");

以上storePath的内容为: D:\Spring_Boot_Test\target\classes\test.store

参考

Stackoverflow: keytool error Keystore was tampered with, or password was incorrect
Access a File from the Classpath in a Spring Application

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

这是一个非常关键且典型的 **生产部署问题**: > ❓ “我把证书导入了本地 JVM 的 `cacerts`,开发时没问题。但打包发版后,用户环境没有这个证书,HTTPS 连接还是会失败。” --- ## ✅ 核心结论(先看答案) > 🔒 **你不能假设目标机器的 JVM 已经信任你的服务器证书。** > > 所以:**不要依赖“手动导入证书到客户机 cacerts”作为发布方案。** > > ✅ 正确做法是: > > 1. **在应用启动时自动加载自定义 truststore**(如 `truststore.jks`),而不是修改全局 `cacerts` > 2. 将该 `truststore.jks` **随应用一起打包**(放在 `resources` 或外部配置目录) > 3. 启动时通过代码或 JVM 参数指定使用这个 truststore --- ## 🧩 场景还原 你有一个 Spring Boot 应用,连接使用自签名证书的 MinIO: ```java MinioClient client = MinioClient.builder() .endpoint("https://minio.example.com:9000") .credentials("xxx", "xxx") .build(); // 抛出 SSLHandshakeException ``` 你在开发机上执行过: ```bash keytool -importcert -keystore $JAVA_HOME/lib/security/cacerts -file minio.crt ... ``` ✅ 开发正常。 ❌ 但打成 jar 包发给客户或部署到新服务器 → 失败! --- ## ✅ 解决方案一:【推荐】将 truststore 打包进 JAR,程序自动加载 ### 步骤 1:创建一个专用的 `truststore.jks` ```bash keytool -importcert \ -alias minio-server \ -file minio-cert.crt \ -keystore src/main/resources/truststore.jks \ -storepass changeit \ -noprompt ``` > ✅ 会生成 `src/main/resources/truststore.jks`,Maven/Gradle 构建时自动打包进 `jar` --- ### 步骤 2:启动时设置系统属性(推荐方式) #### 方式 A:命令行启动时指定(最简单) ```bash java \ -Djavax.net.ssl.trustStore=classpath:truststore.jks \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar your-app.jar ``` ⚠️ 错!`classpath:` 不被 JVM 原生支持。 👉 必须提供**绝对路径或相对文件路径**。 所以你应该这样做: ```bash # 假设 truststore 放在 jar 同级目录 java \ -Djavax.net.ssl.trustStore=./truststore.jks \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar your-app.jar ``` 但这要求你把 `truststore.jks` 放在外面。 --- ### ✅ 推荐做法:在代码中动态加载 TrustManager(可从 classpath 读取) ```java @Configuration public class SslConfig { public static void setupCustomTrustStore() throws Exception { // 从 classpath 加载 truststore InputStream is = SslConfig.class.getClassLoader() .getResourceAsStream("truststore.jks"); if (is == null) { throw new IllegalStateException("truststore.jks not found in classpath"); } KeyStore trustStore = KeyStore.getInstance("JKS"); trustStore.load(is, "changeit".toCharArray()); is.close(); // 初始化 TrustManagerFactory TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 设置为默认 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); SSLContext.setDefault(sslContext); // ⚠️ 可能需要安全管理器权限 } } ``` ### 在主类中调用: ```java @SpringBootApplication public class MyApplication { public static void main(String[] args) { try { SslConfig.setupCustomTrustStore(); // 👈 启动时安装信任库 } catch (Exception e) { throw new RuntimeException("Failed to initialize custom trust store", e); } SpringApplication.run(MyApplication.class, args); } } ``` ✅ 现在即使目标机器没导入证书,也能安全建立 HTTPS 连接。 --- ## ✅ 解决方案二:构建时复制 truststore 到输出目录(自动化) 确保 `truststore.jks` 被复制到最终发布的包附近。 ### Maven 配置:自动复制资源到 `target/classes` ```xml <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.jks</include> <include>**/*.crt</include> </includes> </resource> </resources> </build> ``` 然后你可以写脚本启动: ```bash #!/bin/bash JAR_PATH="target/myapp.jar" TRUSTSTORE_PATH="target/classes/truststore.jks" java \ -Djavax.net.ssl.trustStore="$TRUSTSTORE_PATH" \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar "$JAR_PATH" ``` 📦 发布时只需包含: - `myapp.jar` - `truststore.jks` - `startup.sh` --- ## ✅ 解决方案三:容器化部署(Docker)预装证书 ```dockerfile FROM openjdk:17-jre-slim COPY target/myapp.jar /app.jar COPY src/main/resources/truststore.jks /truststore.jks # 设置默认 trustStore ENV JAVA_OPTS="-Djavax.net.ssl.trustStore=/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit" ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"] ``` ✅ 构建即信任,无需客户干预。 --- ## ✅ 方案对比总结 | 方案 | 是否需客户操作 | 安全性 | 推荐度 | |------|----------------|--------|--------| | 修改客户机 `cacerts` | ✅ 需管理员操作 | 中 | ⭐ | | 自定义 `truststore.jks` + JVM 参数 | ✅ 需部署脚本配合 | 高 | ⭐⭐⭐⭐ | | 代码中从 classpath 动态加载 TrustManager | ❌ 无需客户操作 | 高 | ⭐⭐⭐⭐⭐ | | Docker 镜像内置信任 | ❌ 一键部署 | 最高 | ⭐⭐⭐⭐⭐ | --- ## 💡 最佳实践建议 ### 如果你是 **软件供应商 / ISV** 👉 推荐做法: ```text [你的应用 JAR] + [truststore.jks] ← 包含你所依赖服务的 CA 或服务器证书 + [startup.sh / startup.bat] ← 设置 -Djavax.net.ssl.trustStore=... ``` 文档说明: > “请使用提供的启动脚本运行本应用,它已配置必要的 HTTPS 信任策略。” --- ## ⚠️ 安全提醒 - 不要使用“忽略所有证书”的 `X509TrustManager` - 使用 `truststore.jks` 只信任你预期的服务 - 定期更新证书和 truststore - 若服务证书变更,请同步更新并重新发布 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值