惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
C
Cyber Attacks, Cyber Crime and Cyber Security
G
GRAHAM CLULEY
T
Tenable Blog
T
Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
Intezer
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
K
Kaspersky official blog
Security Latest
Security Latest
P
Privacy & Cybersecurity Law Blog
Google Online Security Blog
Google Online Security Blog
SecWiki News
SecWiki News
P
Palo Alto Networks Blog
TaoSecurity Blog
TaoSecurity Blog
Webroot Blog
Webroot Blog
Spread Privacy
Spread Privacy
O
OpenAI News
The Last Watchdog
The Last Watchdog
P
Proofpoint News Feed
C
Check Point Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
人人都是产品经理
人人都是产品经理
S
Security @ Cisco Blogs
Scott Helme
Scott Helme
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
月光博客
月光博客
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
T
Troy Hunt's Blog
W
WeLiveSecurity
GbyAI
GbyAI
N
News | PayPal Newsroom
Y
Y Combinator Blog
C
Cisco Blogs
H
Help Net Security
The GitHub Blog
The GitHub Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 【当耐特】
Jina AI
Jina AI
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
云风的 BLOG
云风的 BLOG
小众软件
小众软件
N
News and Events Feed by Topic

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
Spring Boot mTLS 报 `keystore password was incorrect`:不一定是密码错了
devpotato · 2026-05-01 · via 博客园_首页

最近在给一个 Spring Boot 服务接入 mTLS 时,遇到了一个比较容易误导人的问题:本地调试正常,但服务部署到 Kubernetes Pod 后,访问 mTLS 接口失败,并出现下面的异常。

Caused by: java.io.IOException: keystore password was incorrect

Caused by: java.security.UnrecoverableKeyException:
failed to decrypt safe contents entry:
javax.crypto.BadPaddingException:
Given final block not properly padded.
Such issues can arise if a bad key is used during decryption.

第一眼看这个异常,很容易判断为:keystore 密码配置错了。

但实际排查下来,密码并不是唯一原因。只要 JVM 在加载 .p12 文件时无法正确解密其中内容,都可能被包装成类似的异常。例如:

  • 证书文件在 Maven 打包过程中被破坏;
  • 本地JDK 和 Pod 中的JDK 版本不一致,导致 PKCS12 兼容性问题;
  • key-store password、key password、trust-store password 配置混淆;
  • 运行环境实际加载的证书文件不是预期文件;
  • 访问端口配置错误,导致问题被误判为 mTLS 失败。

本文记录这次排查过程,也整理一套 Spring Boot mTLS 落地时比较实用的检查清单。


一、先区分:这是证书加载问题,还是网络访问问题

mTLS 相关问题通常可以分成两个阶段。

1.1 服务启动阶段

Spring Boot 启动时会根据配置加载服务端证书:

server:
  ssl:
    key-store: classpath:certs/dev/keystore.p12
    key-store-password: xxx
    key-store-type: PKCS12
    key-alias: xxx

如果这个阶段加载 keystore 失败,通常会在应用启动日志中看到 IOExceptionUnrecoverableKeyExceptionBadPaddingException 之类的异常。

这类问题一般和下面因素有关:

  • 证书文件是否存在;
  • 证书文件是否被破坏;
  • keystore 密码是否正确;
  • key password 是否正确;
  • key alias 是否正确;
  • JDK 是否能兼容当前 PKCS12 文件。

1.2 请求访问阶段

如果服务已经启动成功,但客户端访问失败,问题可能出在:

  • 客户端没有携带证书;
  • 客户端证书不被服务端 truststore 信任;
  • 服务端证书主机名不匹配;
  • 端口没有开放;
  • 网关、负载均衡、安全组没有放通对应端口。

这两类问题要分开看。
比如端口没有开放,一般会表现为连接超时、连接拒绝、502、503,而不是 BadPaddingException


二、问题一:Maven 打包破坏了 .p12 证书文件

这次遇到的第一个问题,是证书文件在 Maven 打包过程中发生了变化。

.p12 是二进制文件,不能像普通文本配置一样做资源过滤。如果项目中启用了 Maven resource filtering,Maven 可能会尝试替换资源文件里的占位符,或者按文本编码处理资源文件。对于 .p12 这类二进制文件来说,这可能直接破坏文件内容。

破坏之后,JVM 加载 keystore 时就可能出现:

java.io.IOException: keystore password was incorrect
java.security.UnrecoverableKeyException
javax.crypto.BadPaddingException

这时异常提示的是“密码错误”,但根因可能是“文件已经不是原来的文件”。

2.1 Maven 配置修复

可以在 pom.xml 中显式排除 .p12 文件的过滤处理:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.3.1</version>
            <configuration>
                <encoding>UTF-8</encoding>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>p12</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
</build>

如果项目统一管理 Maven 插件版本,也可以把版本放到 pluginManagement 中。

2.2 如何确认证书是否被打包破坏

不要只看文件名是否存在,应该比较打包前后的文件哈希。

例如:

shasum -a 256 src/main/resources/certs/dev/keystore.p12

再从 jar 包中解压出对应文件:

jar xf target/app.jar BOOT-INF/classes/certs/dev/keystore.p12
shasum -a 256 BOOT-INF/classes/certs/dev/keystore.p12

如果两个哈希不一致,就说明打包后的证书文件发生了变化。

2.3 生产环境建议

开发环境把证书放在 src/main/resources 下可以降低调试成本,但生产环境不建议把证书直接打进 jar 包。

更推荐的方式是:

  • Kubernetes Secret 挂载证书文件;
  • 使用 file:/path/to/keystore.p12 指向挂载路径;
  • 密码通过环境变量、配置中心或 Secret 注入;
  • 不把证书和密码固化在应用制品中。

例如:

server:
  ssl:
    key-store: file:/etc/certs/server/keystore.p12
    key-store-password: ${TLS_KEY_STORE_PASSWORD}
    key-store-type: PKCS12
    trust-store: file:/etc/certs/server/truststore.p12
    trust-store-password: ${TLS_TRUST_STORE_PASSWORD}
    trust-store-type: PKCS12

这样可以减少“同一个 jar 在不同环境需要不同证书”的维护成本,也更符合容器化部署习惯。


三、问题二:本地和 Pod 中的 JDK 版本不一致

第二个问题是:本地 JDK 和 Pod 镜像中的 JDK 版本不一致。

PKCS12 是标准格式,但具体到 Java 运行时,不同 JDK 版本、不同安全 Provider 对其中加密算法、MAC 算法和证书链解析的支持并不完全一致。

常见场景是:

  • 本地用较新的 JDK 或 OpenSSL 生成 .p12
  • Pod 中使用较旧 JDK 运行;
  • 本地能正常加载,容器中加载失败;
  • 最终表现为 keystore 解密失败或 password incorrect。

3.1 检查运行时 JDK

先确认本地和容器里的 JDK 版本:

java -version

也可以进入 Pod 检查:

kubectl exec -it <pod-name> -- java -version

3.2 用 keytool 验证证书能否被当前 JDK 读取

在运行环境中执行:

keytool -list \
  -storetype PKCS12 \
  -keystore /etc/certs/server/keystore.p12

如果这里都读不出来,Spring Boot 启动时也大概率读不出来。

3.3 工程建议

mTLS 证书生成、验证、运行最好使用一致或兼容的 JDK 版本。至少要保证:

  • 构建环境和运行环境的 JDK 版本明确;
  • 容器基础镜像版本可追踪;
  • 证书生成脚本中记录 JDK/OpenSSL 版本;
  • 升级 JDK 后重新验证证书加载和握手流程。

这类问题不一定每天发生,但一旦发生,排查成本比较高。尤其是服务迁移到新基础镜像、升级 JDK、切换构建机时,建议把证书加载验证加入发布前检查。


四、问题三:8443 端口未开放导致访问失败

第三个问题是端口暴露。

我最开始把 Spring Boot 的 HTTPS 端口配置为 8443,但部署环境没有开放这个端口,导致外部访问失败。后来改成 443 后访问正常。

这里需要注意:端口未开放通常不是 BadPaddingException 的直接原因。它更可能导致:

  • connection refused;
  • connection timeout;
  • 502/503;
  • Ingress 或网关转发失败。

因此排查时要分清楚:

  1. 应用是否启动成功;
  2. keystore/truststore 是否加载成功;
  3. 服务端口是否监听;
  4. 网关是否放通。

五、Spring Boot 服务端 mTLS 配置示例

下面是一个服务端开启 mTLS 的 Spring Boot 配置示例。

5.1 pom文件配置

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.0.2</version>
            <configuration>
                <encoding>UTF-8</encoding>
                <!-- 过滤证书文件 -->
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>p12</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
</build>

5.2 yml文件配置

server:
  port: 443
  ssl:
    enabled: true

    # KeyStore:服务端自己的证书和私钥
    key-store: classpath:certs/dev/keystore.p12
    key-store-password: xxx
    key-store-type: PKCS12
    key-alias: server
    key-password: xxx

    # TrustStore:服务端信任的 CA,用于验证客户端证书
    trust-store: classpath:certs/dev/truststore.p12
    trust-store-password: xxx
    trust-store-type: PKCS12

    enabled-protocols: TLSv1.2,TLSv1.3

    # need 表示客户端必须提供证书
    client-auth: need

几个配置需要特别注意。

key-store-passwordkey-password 不一定是同一个

  • key-store-password:打开 keystore 文件的密码;
  • key-password:读取 keystore 中私钥条目的密码。

很多时候两者相同,所以容易被忽略。但如果生成证书时两者不同,配置错 key-password 也可能导致 UnrecoverableKeyException

trust-store 决定服务端信任哪些客户端证书

开启 mTLS 后,服务端不只是提供自己的证书,还要验证客户端证书是否可信。

服务端会使用 trust-store 判断客户端证书链是否可信。如果客户端证书不是由 truststore 中的 CA 签发,或者证书链不完整,握手会失败。

client-auth: needclient-auth: want 的区别

  • need:客户端必须提供证书,否则握手失败;
  • want:服务端会请求客户端证书,但客户端不提供也可能继续握手;
  • none:不进行客户端证书认证。

如果目标是强制 mTLS,应该使用 need

5.3 证书文件位置

5.4 是否要同时支持 HTTP 和 HTTPS

如果是已经上线的服务,直接切换到 mTLS 可能会影响旧客户端。为了兼容迁移,有时会临时同时开放 HTTP 和 HTTPS。

Spring Boot 使用内置 Tomcat 时,可以额外增加一个 HTTP Connector:

@Configuration
public class HttpConnectorConfig {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addAdditionalTomcatConnectors(createHttpConnector());
        return factory;
    }

    private Connector createHttpConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(8080);
        connector.setSecure(false);
        return connector;
    }
}

但这个方案不能简单理解为“兼容一下就行”。

额外开放 HTTP 端口意味着:这个端口上的请求不会经过 TLS 握手,也不会进行客户端证书认证。如果 HTTP 端口可以访问同样的业务接口,就可能绕过 mTLS。

更稳妥的做法是:

  • 只在迁移期短暂保留 HTTP;
  • 对 HTTP 端口做网络层限制,只允许内网或指定网关访问;
  • 敏感接口不要通过 HTTP 暴露;
  • 配合 Spring Security 做应用层鉴权;
  • 在网关层做 HTTP 到 HTTPS 的重定向;
  • 迁移完成后关闭 HTTP 端口。

mTLS 解决的是传输层的双向身份认证,不应该被一个额外开放的明文端口绕过去。


六、HttpClient 访问 mTLS 服务示例

客户端访问 mTLS 服务时,需要准备两类证书材料:

  1. 客户端 key-store:包含客户端证书和私钥,用于向服务端证明“我是谁”;
  2. 客户端 trust-store:包含服务端证书的 CA,用于验证“服务端是否可信”。

6.1 引入 httpclient 依赖:

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1.4</version>
</dependency>

6.2 证书文件位置

6.3 client类开发

public class MTLSClient {
    // 密钥库/信任库密码(建议从配置文件读取,不要硬编码)
    private static final String KEY_STORE_PASSWORD = "xxx";
    private static final String TRUST_STORE_PASSWORD = "xxx";

    public static void main(String[] args) {
        try {
            // 替换为你生成的 KeyStore 文件路径
            String clientKeyStorePath = "certs/client.p12";
            String trustStorePath = "certs/truststore.p12";

            // 1. 创建 SSLContext
            SSLContext sslContext = createMTLSSSLContext(clientKeyStorePath, trustStorePath);

            // 创建 SSL 连接工厂,使用自定义主机名验证器
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    new String[]{"TLSv1.2", "TLSv1.3"}, // 指定协议
                    null,
                    new AllowAllHostnameVerifier()
            );

            // 连接池管理器
            HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                    .setSSLSocketFactory(sslSocketFactory)
                    .setMaxConnTotal(20)
                    .setMaxConnPerRoute(10)
                    .build();

            // 2. 创建 HttpClient
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setConnectionManager(connectionManager)
                    .build();

            // 3. 构建请求(替换为你的 mTLS 服务端地址)
            HttpPost httpPost = new HttpPost("https://xxx:443/");
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setEntity(new StringEntity("请求体 body"));

            // 4. 发送请求并处理响应
            CloseableHttpResponse response = httpClient.execute(httpPost);
            System.out.println("响应状态码: " + response.getCode());
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String responseBody = EntityUtils.toString(entity);
                System.out.println("响应体: " + responseBody);
                EntityUtils.consume(entity);
            }
        } catch (Exception e) {
            System.err.println("mTLS 请求失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 从 KeyStore 文件创建 mTLS 专用的 SSLContext
     */
    public static SSLContext createMTLSSSLContext(String clientKeyStorePath, String trustStorePath) throws Exception {
        // 1. 加载客户端密钥库(PKCS12 格式)
        KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
        Resource clientKeyStoreResource = new ClassPathResource(clientKeyStorePath);
        try (InputStream keyStoreIn = clientKeyStoreResource.getInputStream()) {
            clientKeyStore.load(keyStoreIn, KEY_STORE_PASSWORD.toCharArray());
        }

        // 2. 加载信任库(PKCS12 格式)
        KeyStore trustStore = KeyStore.getInstance("PKCS12");
        Resource trustStoreResource = new ClassPathResource(trustStorePath);
        try (InputStream trustStoreIn = trustStoreResource.getInputStream()) {
            trustStore.load(trustStoreIn, TRUST_STORE_PASSWORD.toCharArray());
        }

        // 3. 构建 SSLContext(自动加载密钥库和信任库)
        return SSLContexts.custom()
                .loadKeyMaterial(clientKeyStore, KEY_STORE_PASSWORD.toCharArray()) // 客户端证书+私钥
                .loadTrustMaterial(trustStore, null) // 信任CA证书
                .build();
    }

    /**
     * 自定义主机名验证器,用于开发环境绕过主机名验证
     */
    private static class AllowAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // 接受所有主机名
        }
    }
}

这里不建议在生产环境使用“跳过主机名校验”的 HostnameVerifier

这会让客户端接受任意主机名,即使 mTLS 能验证客户端证书,也不代表可以跳过服务端证书的主机名校验。否则客户端可能信任了一个证书链合法但域名不匹配的服务端。

如果是本地调试,可以临时关闭校验,但应该把它限制在测试代码或开发 profile 中,不能进入生产默认路径。


8. 线上排查清单

遇到 keystore password was incorrect 时,不要只盯着密码,可以按下面顺序排查。

排查项 说明
证书文件路径 确认运行环境实际加载的是哪个 keystore/truststore
文件是否存在 classpath、挂载路径、Secret volume 是否正确
文件是否被破坏 比较打包前后或挂载前后的 sha256
store password 检查 key-store-passwordtrust-store-password
key password 检查 key-password 是否和私钥条目密码一致
alias 检查 key-alias 是否存在于 keystore 中
store type 明确使用 PKCS12,避免依赖默认类型
JDK 版本 对比本地、构建环境、Pod 运行环境
keytool 验证 在容器内使用 keytool 直接读取证书
Maven 资源过滤 .p12.jks 等二进制文件不要被 filtering 处理
容器镜像 确认基础镜像中的 JDK 和安全 Provider
端口暴露 区分 SSL 加载失败和网络访问失败
Service/Ingress 检查 targetPort、port、TLS 转发方式
客户端证书 确认客户端是否携带证书和私钥
truststore 内容 服务端是否信任客户端证书签发 CA
主机名校验 服务端证书 CN/SAN 是否和访问域名匹配