Spring Boot 从 HTTP 升级 HTTPS:我踩了 3 个大坑,终于解决了!
Spring Boot 从 HTTP 升级 HTTPS:我踩了 3 个大坑,终于解决了!
摘要:本文详细记录了在 Spring Boot 项目中将 HTTP 服务升级为 HTTPS 过程中遇到的各种问题和解决方案,包括 SSL 证书生成、Maven 资源过滤导致的证书损坏、JDK 版本兼容性等常见问题。希望能帮助遇到类似问题的开发者快速定位和解决问题。
关键词:Spring Boot、HTTPS、SSL、PKCS12、Maven、DerInputStream
一、背景介绍
最近在给一个 Spring Boot 项目开启 HTTPS 时,本以为只是加几行配置、生成一个证书就能搞定。
结果实际操作下来,连续踩了几个坑:
keystore.p12无法加载DerInputStream.getLength(): lengthTag=111, too big- IntelliJ IDEA 可以运行,Maven 打包后却启动失败
- 证书文件明明存在,但运行时总报损坏
最后排查了很久,才发现真正的问题居然是:
Maven 资源过滤把
.p12二进制证书文件给“改坏了”。
这篇文章会把整个排查过程、根因分析以及最终解决方案完整记录下来,希望能帮你少踩坑。
环境信息
- Spring Boot 版本:3.x
- JDK 版本:Corretto 21
- 构建工具:Maven 3.x
- IDE:IntelliJ IDEA 2024
- 证书类型:PKCS12 (.p12)
二、基础配置
2.1 application.yml 配置
在 application.yml 中添加 SSL 配置:
server:
port: 8081
ssl:
key-store: classpath:keystore.p12
key-store-password: 123456
key-store-type: PKCS12
key-alias: local-ssl
enabled: true
配置说明:
key-store: 证书文件路径,classpath:表示从类路径加载key-store-password: 证书密码key-store-type: 证书类型,推荐使用 PKCS12key-alias: 证书别名enabled: 是否启用 SSL
2.2 生成自签名证书
使用 JDK 自带的 keytool 工具生成测试用的自签名证书:
keytool -genkeypair \
-alias local-ssl \
-keyalg RSA \
-keysize 2048 \
-storetype PKCS12 \
-keystore keystore.p12 \
-storepass 123456 \
-keypass 123456 \
-validity 3650 \
-dname "CN=localhost,OU=Development,O=Company,L=City,ST=State,C=CN" \
-ext SAN=dns:localhost,ip:127.0.0.1
参数说明:
-alias: 证书别名,需与配置文件中的key-alias一致-keyalg: 密钥算法,RSA 是最常用的-keysize: 密钥长度,2048 位是推荐的最小值-storetype: 密钥库类型,PKCS12 是行业标准格式-validity: 有效期(天),3650 表示 10 年-dname: 证书持有者信息,CN(Common Name)必须为 localhost-ext SAN: Subject Alternative Name,支持多个域名和 IP
验证证书:
keytool -list -keystore keystore.p12 -storetype PKCS12 -storepass 123456
输出示例:
如果能正常输出证书信息,说明证书本身没有问题。
密钥库类型: PKCS12
密钥库提供方: SUN
您的密钥库包含 1 个条目
local-ssl, 2026年5月23日, PrivateKeyEntry,
证书指纹 (SHA-256): 46:2F:7F:73:78:35:A1:6E:6F:AD:27:8F:57:B1:75:8B:38:3E:7A:61:B8:34:E8:4B:F9:2C:B8:76:00:CD:7D:E0
三、遇到的问题及解决方案
第一个坑:DerInputStream.getLength() 报错
问题描述
项目启动直接报错:
Unable to create key store
Could not load store from 'classpath:keystore.p12'
Caused by:
java.io.IOException:
DerInputStream.getLength(): lengthTag=111, too big.
问题分析
这个错误表明 keystore.p12 文件的 DER 编码格式有问题。可能的原因:
- 证书文件在传输或保存过程中损坏
- 使用了不兼容的 JDK 版本生成证书
- Maven 资源过滤破坏了二进制文件
解决方案
步骤1:删除损坏的证书文件
# Windows PowerShell
Remove-Item E:\projects\myapp\service-web\src\main\resources\keystore.p12
步骤2:重新生成证书
cd E:\projects\myapp\service-web\src\main\resources
# 使用项目指定的 JDK 21
C:\Users\84869\.jdks\corretto-21.0.6\bin\keytool.exe -genkeypair ^
-alias local-ssl ^
-keyalg RSA ^
-keysize 2048 ^
-storetype PKCS12 ^
-keystore keystore.p12 ^
-storepass 123456 ^
-keypass 123456 ^
-validity 3650 ^
-dname "CN=localhost" ^
-ext SAN=dns:localhost,ip:127.0.0.1 ^
-noprompt
步骤3:验证新生成的证书
C:\Users\84869\.jdks\corretto-21.0.6\bin\keytool.exe -list ^
-keystore keystore.p12 ^
-storetype PKCS12 ^
-storepass 123456
第二个坑:JDK 版本不一致
问题描述
即使重新生成了证书,启动时仍然报同样的错误。经过深入排查,发现是 JDK 版本不一致 导致的问题。
根本原因
- 系统环境变量指向的是 Java 8(
java -version显示 1.8.0_281) - IntelliJ IDEA 配置使用的是 JDK 21(Corretto 21.0.6)
- 之前用 Java 8 的 keytool 生成的证书与 JDK 21 运行时不兼容
关键知识点:
- Java 8 和 Java 21 的 PKCS12 实现有差异
- Java 8 对 PKCS12 的支持有限,直接使用
-storetype PKCS12生成的证书可能与其他版本不兼容 - 不同版本的 JDK 使用的加密算法和默认参数可能不同
解决方案
方案一:统一使用 JDK 21(推荐)
-
检查 IntelliJ IDEA 配置
- 打开
File→Project Structure(Ctrl+Alt+Shift+S) - 在
Project标签下:- SDK: 选择
corretto-21.0.6 - Language level: 选择
21
- SDK: 选择
- 打开
-
配置运行配置的 JRE
- 打开
Run→Edit Configurations - 找到启动配置(ServletInitializer)
- 在
JRE下拉框中选择corretto-21.0.6 - 点击
Apply和OK
- 打开
-
设置 Maven 导入器 JDK
- 打开
File→Settings→Build, Execution, Deployment→Build Tools→Maven→Importing - 设置
JDK for importer为corretto-21.0.6
- 打开
-
设置编译器 JDK
- 打开
File→Settings→Build, Execution, Deployment→Compiler→Java Compiler - 设置
Project bytecode version为21
- 打开
-
使用 JDK 21 的 keytool 重新生成证书
C:\Users\84869\.jdks\corretto-21.0.6\bin\keytool.exe -genkeypair ^
-alias local-ssl ^
-keyalg RSA ^
-keysize 2048 ^
-storetype PKCS12 ^
-keystore keystore.p12 ^
-storepass 123456 ^
-keypass 123456 ^
-validity 3650 ^
-dname "CN=localhost" ^
-ext SAN=dns:localhost,ip:127.0.0.1
方案二:临时禁用 SSL(仅用于快速测试)
如果急需启动项目进行其他测试,可以临时禁用 SSL:
server:
ssl:
enabled: false # 临时禁用
然后通过 HTTP 访问:http://localhost:8081/service-web
真正的大坑:Maven 把 .p12 文件过滤坏了
问题描述
配置好 JDK 版本并重新生成证书后,通过 IntelliJ IDEA 直接运行仍然报错。但奇怪的是,源代码目录中的证书文件是正常的。
深入排查
检查源文件和编译后的文件:
# 检查源文件
$srcKeystore = "E:\projects\myapp\service-web\src\main\resources\keystore.p12"
Write-Host "Source size: $((Get-Item $srcKeystore).Length) bytes"
# 检查编译后的文件
$targetKeystore = "E:\projects\myapp\service-web\target\classes\keystore.p12"
Write-Host "Target size: $((Get-Item $targetKeystore).Length) bytes"
# 比较文件哈希值
if ((Get-FileHash $srcKeystore).Hash -eq (Get-FileHash $targetKeystore).Hash) {
Write-Host "Files are IDENTICAL" -ForegroundColor Green
} else {
Write-Host "Files are DIFFERENT!" -ForegroundColor Red
}
输出结果:
Source size: 2606 bytes
Target size: 4883 bytes
Files are DIFFERENT!
发现问题! Maven 编译后的证书文件大小发生了变化,说明 Maven 的资源处理过程破坏了证书文件。
根本原因
查看 pom.xml 中的资源配置:
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/config_generate</exclude>
<exclude>**/metrics.war</exclude>
<exclude>**/*.sh</exclude>
<exclude>static/**</exclude>
</excludes>
<filtering>true</filtering> <!-- 启用资源过滤 -->
</resource>
</resources>
问题分析:
- Maven Resources Plugin 默认会对所有资源文件进行文本过滤(filtering=true)
- 文本过滤会将
${variable}占位符替换为实际值 - 对于二进制文件(如 .p12 证书),文本过滤会破坏其字节结构
- 虽然配置了
nonFilteredFileExtensions,但老版本的 Maven Resources Plugin(2.6)不支持此配置
Maven 资源过滤的工作原理:
原始 keystore.p12: 30 82 0A CA 02 01 03 ... (2606 bytes) ✅
经过 Maven 处理后: 30 82 13 1B 02 01 03 ... (4883 bytes) ❌ 被破坏!
Maven 尝试将二进制文件当作 UTF-8 文本处理,导致:
- 文件大小改变
- 字节内容损坏
- DER 编码解析失败
解决方案
修改 pom.xml,明确排除证书文件不被过滤:
<resources>
<!-- 第一组:处理文本文件,启用变量替换,但排除二进制文件 -->
<resource>
<directory>src/main/resources</directory>
<excludes>
<!-- 排除证书文件,防止被文本过滤破坏 -->
<exclude>**/*.p12</exclude>
<exclude>**/*.jks</exclude>
<exclude>**/*.keystore</exclude>
</excludes>
<filtering>true</filtering>
</resource>
<!-- 第二组:原样复制二进制文件,不做任何处理 -->
<resource>
<directory>src/main/resources</directory>
<includes>
<!-- 包含证书文件,确保原样复制 -->
<include>**/*.p12</include>
<include>**/*.jks</include>
<include>**/*.keystore</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
验证修复效果:
# 清理并重新编译
mvn clean resources:resources -pl service-web -DskipTests
# 检查文件一致性
Source size: 2606 bytes
Target size: 2606 bytes
SUCCESS: Files are IDENTICAL!
# 验证证书有效性
keytool -list -keystore target/classes/keystore.p12 -storetype PKCS12 -storepass 123456
输出:
密钥库类型: PKCS12
密钥库提供方: SUN
您的密钥库包含 1 个条目
local-ssl, 2026年5月23日, PrivateKeyEntry,
证书指纹 (SHA-256): 46:2F:7F:73:78:35:A1:6E:6F:AD:27:8F:57:B1:75:8B:38:3E:7A:61:B8:34:E8:4B:F9:2C:B8:76:00:CD:7D:E0
✅ 问题解决!
四、完整的配置方案
4.1 项目结构
service-web/
├── src/
│ └── main/
│ ├── java/
│ │ └── com/example/paas/startup/
│ │ └── ServletInitializer.java
│ └── resources/
│ ├── application.yml # 主配置文件
│ ├── keystore.p12 # SSL 证书文件
│ └── ...
├── pom.xml # Maven 配置
└── target/
└── classes/
├── application.yml # 编译后的配置
└── keystore.p12 # 编译后的证书(应与源文件一致)
4.2 application.yml 完整配置
server:
port: 8081
ssl:
key-store: classpath:keystore.p12
key-store-password: 123456
key-store-type: PKCS12
key-alias: local-ssl
enabled: true
# 其他配置...
4.3 pom.xml 资源配置
<build>
<resources>
<!-- 文本文件:启用变量替换 -->
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.p12</exclude>
<exclude>**/*.jks</exclude>
<exclude>**/*.keystore</exclude>
</excludes>
<filtering>true</filtering>
</resource>
<!-- 二进制文件:原样复制 -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.p12</include>
<include>**/*.jks</include>
<include>**/*.keystore</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<!-- 其他插件配置... -->
</plugins>
</build>
4.4 证书生成脚本(Windows)
创建 generate-keystore.bat:
@echo off
echo ========================================
echo 生成 SSL 自签名证书
echo ========================================
set JAVA_HOME=C:\Users\84869\.jdks\corretto-21.0.6
set KEYTOOL=%JAVA_HOME%\bin\keytool.exe
set KEYSTORE_PATH=E:\projects\myapp\service-web\src\main\resources\keystore.p12
echo.
echo 正在删除旧证书...
if exist %KEYSTORE_PATH% del %KEYSTORE_PATH%
echo.
echo 正在生成新证书...
%KEYTOOL% -genkeypair ^
-alias local-ssl ^
-keyalg RSA ^
-keysize 2048 ^
-storetype PKCS12 ^
-keystore %KEYSTORE_PATH% ^
-storepass 123456 ^
-keypass 123456 ^
-validity 3650 ^
-dname "CN=localhost,OU=Development,O=Company,L=City,ST=State,C=CN" ^
-ext SAN=dns:localhost,ip:127.0.0.1 ^
-noprompt
echo.
echo 验证证书...
%KEYTOOL% -list -keystore %KEYSTORE_PATH% -storetype PKCS12 -storepass 123456
echo.
echo ========================================
echo 证书生成完成!
echo ========================================
pause
4.5 证书生成脚本(Linux/Mac)
创建 generate-keystore.sh:
#!/bin/bash
echo "========================================"
echo "生成 SSL 自签名证书"
echo "========================================"
JAVA_HOME=/usr/lib/jvm/corretto-21
KEYTOOL=$JAVA_HOME/bin/keytool
KEYSTORE_PATH=./src/main/resources/keystore.p12
echo ""
echo "正在删除旧证书..."
rm -f $KEYSTORE_PATH
echo ""
echo "正在生成新证书..."
$KEYTOOL -genkeypair \
-alias local-ssl \
-keyalg RSA \
-keysize 2048 \
-storetype PKCS12 \
-keystore $KEYSTORE_PATH \
-storepass 123456 \
-keypass 123456 \
-validity 3650 \
-dname "CN=localhost,OU=Development,O=Company,L=City,ST=State,C=CN" \
-ext SAN=dns:localhost,ip:127.0.0.1 \
-noprompt
echo ""
echo "验证证书..."
$KEYTOOL -list -keystore $KEYSTORE_PATH -storetype PKCS12 -storepass 123456
echo ""
echo "========================================"
echo "证书生成完成!"
echo "========================================"
五、最佳实践建议
5.1 开发环境
-
使用自签名证书
- 方便快速搭建和测试
- 注意浏览器会显示安全警告
- 需要手动接受风险继续访问
-
证书管理
- 将
keystore.p12加入.gitignore,避免提交到版本控制 - 提供证书生成脚本,团队成员可自行生成
- 文档化证书生成步骤和密码
- 将
-
JDK 版本统一
- 确保开发团队使用相同的 JDK 版本
- 在项目中明确指定所需的 JDK 版本
- 使用
.idea/misc.xml或pom.xml固定 JDK 版本
5.2 生产环境
-
使用正式 CA 签发的证书
- 推荐 Let’s Encrypt(免费)
- 或购买商业证书(DigiCert、GlobalSign 等)
- 确保证书链完整
-
证书安全管理
- 不要硬编码密码:使用环境变量或配置中心
- 定期更换证书和密码
- 使用强密码策略(至少 12 位,包含大小写字母、数字、特殊字符)
-
配置优化
server:
ssl:
key-store: classpath:keystore.p12
key-store-password: ${SSL_KEYSTORE_PASSWORD} # 从环境变量读取
key-store-type: PKCS12
key-alias: production-ssl
enabled: true
# 启用 TLS 1.2 和 1.3
protocol: TLSv1.2,TLSv1.3
# 配置加密套件
ciphers: >-
TLS_AES_256_GCM_SHA384,
TLS_CHACHA20_POLY1305_SHA256,
TLS_AES_128_GCM_SHA256
- HTTP 自动跳转到 HTTPS
如果需要实现 HTTP 自动跳转到 HTTPS,添加配置类:
@Configuration
public class HttpsConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
// 添加 HTTP 连接器,自动跳转到 HTTPS
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080); // HTTP 端口
connector.setSecure(false);
connector.setRedirectPort(8081); // HTTPS 端口
tomcat.addAdditionalTomcatConnectors(connector);
return tomcat;
}
}
5.3 常见问题排查清单
遇到问题时的检查顺序:
- ✅ 证书文件是否存在且未损坏
- ✅ 证书密码是否正确
- ✅ 证书别名是否与配置一致
- ✅ JDK 版本是否统一
- ✅ Maven 是否正确处理了证书文件
- ✅ IDE 运行配置是否使用了正确的 JDK
- ✅ 端口是否被占用
- ✅ 防火墙是否允许该端口
快速诊断脚本:
# 检查证书文件
$keystore = "src\main\resources\keystore.p12"
if (Test-Path $keystore) {
Write-Host "✓ 证书文件存在" -ForegroundColor Green
Write-Host " 大小: $((Get-Item $keystore).Length) bytes"
# 尝试验证证书
try {
& keytool -list -keystore $keystore -storetype PKCS12 -storepass 123456 | Out-Null
Write-Host "✓ 证书有效" -ForegroundColor Green
} catch {
Write-Host "✗ 证书损坏或密码错误" -ForegroundColor Red
}
} else {
Write-Host "✗ 证书文件不存在" -ForegroundColor Red
}
# 检查 JDK 版本
Write-Host "`nJDK 版本:"
& java -version
# 检查端口占用
Write-Host "`n检查端口 8081:"
$port = Get-NetTCPConnection -LocalPort 8081 -ErrorAction SilentlyContinue
if ($port) {
Write-Host "✗ 端口已被占用" -ForegroundColor Red
} else {
Write-Host "✓ 端口可用" -ForegroundColor Green
}
六、总结
核心要点回顾
-
证书生成
- 使用与运行时相同版本的 JDK 生成证书
- 推荐使用 PKCS12 格式(行业标准)
- 确保 CN 字段为 localhost 或实际域名
-
Maven 配置
- 关键点:必须排除二进制文件(.p12、.jks 等)不被文本过滤
- 使用两组资源配置分别处理文本和二进制文件
- 老版本的 Maven Resources Plugin 不支持
nonFilteredFileExtensions
-
JDK 版本一致性
- 确保系统环境变量、IDE 配置、Maven 配置使用相同的 JDK 版本
- Java 8 和 Java 11+/21 的 PKCS12 实现有差异
-
IDE 配置
- IntelliJ IDEA 的运行配置必须指定正确的 JRE
- Maven 导入器和编译器也要使用相同的 JDK
问题根源总结
| 问题 | 根本原因 | 解决方案 |
|---|---|---|
| DerInputStream 错误 | 证书文件被 Maven 资源过滤破坏 | 在 pom.xml 中排除证书文件不被过滤 |
| 证书加载失败 | JDK 版本不一致 | 统一使用 JDK 21 生成和运行 |
| 文件大小变化 | Maven 文本过滤处理二进制文件 | 设置 filtering=false 原样复制 |
| 浏览器安全警告 | 使用自签名证书 | 手动接受或使用正式 CA 证书 |
经验教训
- 二进制文件特殊处理:证书、图片、压缩包等二进制文件不能被 Maven 的文本过滤处理
- 版本一致性重要:JDK、Maven、IDE 的版本配置必须保持一致
- 深入分析错误:不要只看表面错误,要深入分析根本原因
- 文档化配置过程:记录所有配置步骤,方便团队协作和问题回溯
参考资料
附录 证书脚本(Windows)
A. 常用 keytool 命令
# 生成证书
keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650
# 查看证书详情
keytool -list -keystore keystore.p12 -storetype PKCS12 -v
# 导出证书(公钥)
keytool -exportcert -keystore keystore.p12 -alias mycert -file cert.cer
# 导入证书
keytool -importcert -keystore truststore.p12 -alias mycert -file cert.cer
# 删除证书
keytool -delete -keystore keystore.p12 -alias mycert
# 更改密码
keytool -storepasswd -keystore keystore.p12
B. 证书信息解读
别名: local-ssl # 证书的唯一标识
创建日期: 2026年5月23日 # 证书生成时间
条目类型: PrivateKeyEntry # 私钥条目(包含私钥和证书链)
证书链长度: 1 # 自签名证书,只有根证书
所有者: CN=localhost # 证书持有者
发布者: CN=localhost # 证书颁发者(自签名时相同)
序列号: 7a3b2c1d # 证书唯一序列号
有效期开始日期: 2026-05-23 # 生效时间
有效期截止日期: 2036-05-21 # 过期时间
证书指纹:
SHA1: D8:65:66:69:... # SHA1 指纹(已不推荐)
SHA256: 66:25:85:A6:... # SHA256 指纹(推荐)
签名算法名称: SHA384withRSA # 签名算法
主体公共密钥算法: 2048 位 RSA 密钥 # 公钥算法和长度
最后
如果这篇文章帮你解决了问题,欢迎:
- 点赞 👍
- 收藏 ⭐
- 转发 🔁
也欢迎在评论区交流你遇到的 HTTPS 坑。
很多时候:
真正折磨人的,不是 SSL,而是 Maven。😅
更多推荐


所有评论(0)