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

推荐订阅源

Cloudbric
Cloudbric
有赞技术团队
有赞技术团队
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Threat Research - Cisco Blogs
L
LangChain Blog
Simon Willison's Weblog
Simon Willison's Weblog
Project Zero
Project Zero
Latest news
Latest news
S
Schneier on Security
Cisco Talos Blog
Cisco Talos Blog
MyScale Blog
MyScale Blog
C
Check Point Blog
IT之家
IT之家
P
Palo Alto Networks Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
G
Google Developers Blog
T
Tor Project blog
T
Threatpost
D
DataBreaches.Net
博客园 - 【当耐特】
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Troy Hunt's Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
博客园_首页
S
Securelist
T
The Exploit Database - CXSecurity.com
Last Week in AI
Last Week in AI
量子位
U
Unit 42
Know Your Adversary
Know Your Adversary
Hugging Face - Blog
Hugging Face - Blog
S
Security Affairs
Google Online Security Blog
Google Online Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
S
SegmentFault 最新的问题
Engineering at Meta
Engineering at Meta
N
News and Events Feed by Topic
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 分享一下笔者的 Mac 装机必备软件 第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主分片和副本分片概念详解 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,哪个更好? - 苏三说技术
【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响
杨凯凡 · 2026-04-17 · via 博客园_首页

上一篇【001】把 HTTP 这一条「明文应用层」捋了一遍。一旦站点改成 HTTPS,同样还是 HTTP 语义(方法、状态码、Header、JSON),但 TCP 连接建立之后、HTTP 报文发送之前,多了一层 TLS(前身常叫 SSL) 协商:协商出对称密钥、校验服务器身份、(可选)校验客户端。联调时常见的「证书过期」「域名和证书不一致」「小程序要求合法 HTTPS」「Java 调合作伙伴接口 PKIX 报错」大半都落在这层上。

这篇用「能排障、能跟运维对齐、能配 Spring Boot / 反代 / 本地证书」为度做一次加量粗解,仍然不替密码学教材。下面我按「HTTPS 解决啥 → 证书与链 → TLS 版本与握手直觉 → Spring Boot 实操 → 反代与转发头 → Java 当客户端 → 命令行排障 → 自签与 SAN → 双向认证与小场景 → 小程序合法域名」往下聊;每个大节里都尽量塞可复制的配置与命令


1. HTTPS 在解决什么问题 ✉

HTTP 上,链路中的设备若能看到你的流量,就能读到 URL(含敏感 query)、Header、Body(含 token——敏感凭证更应放 Header 而非 query)。HTTPS = HTTP over TLS,TLS 主要负责三件事(和「机密性 / 完整性 / 身份」对应):

  • 机密性:应用数据用协商出的对称密钥加密,窃听者拿到的是密文。
  • 完整性:现代套件多用 AEAD,篡改更容易被对端拒收或校验失败。
  • 身份(对服务端而言最显眼):服务端用 X.509 证书绑定域名(及公钥),客户端验证链与主机名后,才相信「当前连接对端是该域名背后的服务」。

还要知道两个「周边」问题:

混合内容(Mixed Content)
页面是 HTTPS,但页面里的脚本、接口、图片还用 http:// 去拉,浏览器可能拦截或降级处理,表现为「线上环境只有部分资源失败」。前后端联调时,统一入口协议很关键。

HSTS(HTTP Strict Transport Security)
服务端通过响应头要求浏览器只走 HTTPS 访问该域一段时间,可减少「首次仍打到 HTTP」被劫持的风险。配在云/WAF/Nginx/Spring 上都有可能;开发本地一般碰不到,上线要听安全/运维规范。

和 Java 后端的关系:你在内嵌 Tomcat 上开 server.ssl.*,或前面用 Nginx/Ingress 终结 TLS 再回源 HTTP,都要理解证书与域名;Java 作为 HTTPS 客户端RestTemplateRestClient、Feign、SDK)时还会单独碰到 truststore协议/套件不匹配问题。


2. 证书与证书链里有什么 📜

网站给你的材料一般是 X.509。常见存放形态

形态 说明
PEM Base64 + -----BEGIN CERTIFICATE----- 头尾,可读文本,可链式拼接多个证书。
DER 二进制,扩展名不一定是 .der,有时 .crt 既是 PEM 又是 DER,需用 openssl x509 -in file -text -noout 试读。
PKCS#12(.p12 / .pfx) 一个文件里常含私钥 + 证书链(+ 密码保护),Java keytool、Spring key-store-type: PKCS12 都爱用它。

2.1 字段里你最该盯的

概念 你需要知道的
SAN(Subject Alternative Name) 现代浏览器校验主名看 SAN;仅 CN(Common Name) 不含 SAN 的老证书在部分环境会踩坑。你的访问域名/泛域名应出现在 SAN 列表里。
Issuer 颁发者:公共 CA、企业私有 CA、或自签名(Issuer = Subject)。
有效期 notBefore ~ notAfter。过期必挂;建议设到期告警(提前 30 天很常见)。
公钥与用途 与服务器私钥成对;私钥绝不能进代码仓库。
证书链顺序 典型:站点证书中间证书(可多层)→ 根证常已在客户端信任库。服务端配置时要避免「只发了站点证、没发中间证」。

2.2 DV / OV / EV(面试与采购常听)

  • DV:验证域名控制权,签发快,浏览器地址栏一般只显示锁。
  • OV / EV:多组织信息与更严审核,不能当作「绝对安全」,业务安全仍靠你自己(鉴权、防重放、日志等)。

2.3 公网 vs 内网

  • 公网:Let’s Encrypt、DigiCert 等签的链,手机/浏览器/小程序默认可信。
  • 内网/自签:客户端不信任 → PKIX path building failed。正规做法是把企业根 CA 导入各环境的 truststore,或统一用私有 ACME/PKI;开发机临时导入要有限度。

吊销(CRL / OCSP)
浏览器与部分客户端会检查证书是否被吊销;排障时若遇「极少数客户端不行」,除链以外也可让运维侧看一眼吊销可达性(企业环境才多见)。


3. TLS 版本、套件与握手直觉 🔐

3.1 TLS 1.2 和 1.3 的差异(工程师版)

TLS 1.2 TLS 1.3
典型往返(RTT) 完整握手常见 2-RTT(外加 TCP) 1-RTT 完整握手更常见
套件 历史上套件繁杂,配置错误易出现弱套件 废弃大量旧套件,缺省更安全
中间人可见度 设计年代早,需更注意禁用旧协议 行业整体在推 1.3

实践:生产侧听运维/WAF 统一策略;Java 侧若指定过「只允许 TLSv1.2」之类 JVM 参数,升级时要和网关策略对齐,避免 protocol_version / handshake_failure

3.2 握手「剧情」仍是这几步(与 1.2 对齐理解)

  1. ClientHello:协议版本上限、随机数、支持的套件、SNI(你访问的域名)、扩展(ALPN 里可协商 h2/http/1.1 等)。
  2. ServerHello + Certificate:服务端选定参数,下发证书链
  3. 客户端验链、验主机名(证书是否覆盖 SNI 里的名字)。
  4. 密钥交换(ECDHE 等)→ 双方得到会话密钥
  5. Finished 后,应用数据(即 HTTP)才加密传输——【001】里的报文在这之后才出现。

你要记的结论:业务 HTTP 报错与 TLS 报错层级不同curl/浏览器若在握手阶段失败,根本不会到你的 @GetMapping

3.3 SNI(Server Name Indication)为何重要

同一台机、同一个公网 IP 上可能挂成千上万个 HTTPS 虚拟主机,靠的就是 TLS 握手 ClientHello 里带的 SNI老旧的 Java 8 早期或未正确配置 SNI 的客户端,访问多域名 HTTPS 时可能莫名其妙失败;你现在主力 JDK 17 一般问题不大,但排障时心里要有这根弦。

3.4 会话恢复(了解即可)

Session ID / Session Ticket 让同客户端重复建连时少算密钥,体感主要是「复访更快」。深度排障很少从零扣这个,但若 CDN 文档让你开/关 ticket,知道它和「会话复用」有关即可。


4. Spring Boot 3.x 里开 HTTPS(内嵌容器)☕

很多团队 不在 JVM 上终结 TLS,但若你需要「单机直出 HTTPS」或本地对齐生产,可以用下面几类配置。

4.1 PKCS#12(.p12 / .pfx)——最常见

server:
  port: 8443
  ssl:
    enabled: true
    bundle: classpath:keystore.p12
    key-store-password: ${SSL_KEYSTORE_PASSWORD}
    key-store-type: PKCS12
    key-alias: myalias

不同 Boot 小版本里 key-storebundle 的键名请以你当前 3.3.x 官方文档为准(有的示例仍写 server.ssl.key-store)。要点不变:

  • key-store-password:库口令;key-password 若与库口令不同需单独指定。
  • key-alias:一个 p12 里多个别名时必填对。
  • 生产口令走环境变量 / Secret,勿提交 Git。

HTTP 与 HTTPS 同时开:可以用 两套端口(例如 8080 HTTP + 8443 HTTPS),或只做反代层双协议;若只要「强制跳 HTTPS」,多在 网关/Nginx 做 301,比在业务里写过滤器更常见。

4.2 PEM(证书文件 + 私钥文件)

server:
  port: 8443
  ssl:
    certificate: classpath:cert.pem
    certificate-private-key: classpath:key.pem

注意:私钥文件权限在 Linux 上应收紧(仅运行用户可读)。链若分多段,按文档要求拼成服务端认可的 fullchain 形态。

4.3 协议与套件(进阶兜底)

若安全基线要求「仅 TLSv1.2+」或指定套件,常见是通过 内嵌容器定制反向代理统一策略。Boot 层若暴露 server.ssl.enabled-protocols / ciphers(以文档为准),改前务必和运维对齐,避免老式客户端全挂。

4.4 Actuator 与健康检查

Kubernetes 探针常打 HTTP。若你整站只开在 HTTPS,要么:

  • 探针走 独立 management 端口(仅内网),仍 HTTP;要么
  • 探针路径在明文侧单独暴露(由平台规范约束)。

不要想当然把「业务 URL 必须 https」让探针也跟着复杂化,除非平台明确要求。

4.5 信任「反代传来的 https」——Forwarded 头

见下一节与 5.3 联动:X-Forwarded-Proto: httpsX-Forwarded-Host 等会影响 Spring MVC 的绝对 URL 生成Cookie 的 Secure重定向 Location。Boot 3 常用:

server:
  forward-headers-strategy: framework

native——具体取值请对照 当前 minor 文档与你们网关实际发的头。没配时,常见 bug 是:登录后重定向成 http:// 导致无限跳或 Cookie 丢失。


5. 反向代理终结 TLS(更常见的生产形态)📎

5.1 典型拓扑

浏览器 --HTTPS--> 云 LB / Nginx / Ingress --HTTP--> Spring Boot :8080
                                  \--HTTPS mTLS--> (少数场景)

TLS 在「边缘」终结,内网回源可走明文 HTTP(配合安全组/专线策略)。你的 Java 日志里 RemoteAddr 往往是反代 IP,真实客户端在 X-Forwarded-For 里(注意伪造链,信任边界以网关抹写为准)。

5.2 Nginx 配置加深一版

upstream spring_app {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 443 ssl;
    http2 on;
    server_name api.example.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # 按公司基线补 ssl_protocols / ssl_ciphers 等

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://spring_app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

fullchain.pem:站点 + 中间证书;别只拷「叶子证」。

WebSocket:若路径走 WS,要额外 UpgradeConnection 相关头,或使用 Nginx/网关的 websocket 专用示例

5.3 Spring 侧为何要认 Forwarded

场景:用户访问的是 https://api.example.com,网关回源 http://127.0.0.1:8080。Spring 若不知道「外面是 https」,生成的 redirect 可能写成 http://OAuth2 回调表单 actionHATEOAS 等都会歪。配好 forward-headers-strategy只信任你家网关 IP 写的头(防 Spoof),是生产必备意识。

5.4 云厂商四层/七层 LB

七层 LB 往往在控制台上传证书,回源可指定 HTTP;注意 健康检查路径会话保持TLS 只在入口 的组合。细节跟阿里云/腾讯云/AWS 控制台走,此处只强调:证书续期在 LB/CDN 控制台也可能发生,别忘了不仅 Nginx。


6. Java 作为客户端访问 HTTPS 上游 🎯

6.1 默认信任从哪来

JRE/JDK 带着 lib/security/cacerts(密码默认 changeit),里面是公共 CA 根。公网 HTTPS 一般够用。

6.2 keytool:看库、导入根证(示例)

# 列出 cacerts 里别名(路径随 JDK 安装变)

keytool -list -keystore "%JAVA_HOME%\lib\security\cacerts" -storepass changeit

企业根证书导入(示例,别名自定):

keytool -importcert -noprompt -alias corp-root -file corp-root.pem -keystore "%JAVA_HOME%\lib\security\cacerts" -storepass changeit

注意:改系统 cacerts 影响整台机所有 Java 进程,很多公司改为 启动时挂自定义 truststore,或容器镜像层统一管理。

自定义 truststore 启动:

-Djavax.net.ssl.trustStore=E:/dev/corp-truststore.p12
-Djavax.net.ssl.trustStorePassword=yourSecret
-Djavax.net.ssl.trustStoreType=PKCS12

6.3 典型异常与含义

异常 / 日志关键字 更可能原因
PKIX path building failed 不信任颁发链、缺根/中间、或连错环境(测网证书打生产域名)
CertificateExpiredException 证书过期
CertificateNotYetValidException 本机时间不对或证书未生效
No subject alternative DNS name matching 访问的 Host 不在证书 SAN
javax.net.ssl.SSLHandshakeException 协议/套件/证书错误的大箩筐,需打开 -Djavax.net.debug=ssl:handshake(仅排障短暂开)
SunCertPathBuilderException 常与 PKIX 同类,链构造失败

6.4 框架层:RestTemplate / RestClient / Feign

  • 公网 CA:一般零配置。
  • 私有 CA:运维发根证 → 进 truststore(推荐)→ 再谈代码。
  • 「开发环境跳过校验」类 TrustManager不要复制进生产;代码评审应拦。

Feign / HttpClient 若单独配了 SSL 上下文,要确认没有覆盖默认的 SNI 行为(少数老配置会踩坑)。


7. 联调与排障:命令、流程与表 ✅

7.1 curl

看握手与证书(Windows 可把管道后换成 findstr):

curl -v "https://api.example.com/actuator/health" 2>&1 | findstr /i "subject issuer expire SSL certificate"

指定解析(排除 DNS 捣乱):

curl -v --resolve api.example.com:443:203.0.113.10 "https://api.example.com/"

7.2 openssl s_client(有 OpenSSL 的环境)

openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null

关注:证书链有几张Verify return code 是否 0、subject=CN / SAN、OCSP 相关输出(视版本)。

7.3 浏览器

Chrome:锁图标 → 连接是安全的 → 证书有效,看颁发者、有效期、使用者可选名称。
Safari / Firefox 路径略不同,原则一样:链、日期、域名。

7.4 排障流程(可当 check list)

  1. 先分清:是握手失败还是 HTTP 4xx/5xx(握手失败时很多监控探不到业务指标)。
  2. curl -v / openssl s_client出问题的同一网络环境跑(公司出口、用户家宽、机房内网)。
  3. 核对 访问的 Host 与证书 SAN;核对 系统时间
  4. 服务器/Nginx 侧:fullchain 是否真 full;是否改过中间证未重启。
  5. Java 客户端:truststore 是否包含企业根;JVM TLS 版本是否过严或过旧。
  6. 部分移动网络失败:想到 IPv6SNI、过时安卓 WebView、中间设备劫持等(需要抓包与运营侧信息)。

7.5 浏览器错误与可能原因(扩表)

现象 可能原因
NET::ERR_CERT_DATE_INVALID 过期/未生效/本机时间错误
NET::ERR_CERT_COMMON_NAME_INVALID 域名与 SAN 不匹配
ERR_CERT_AUTHORITY_INVALID 自签或私有 CA 未被信任
NET::ERR_SSL_VERSION_OR_CIPHER_MISMATCH 协议/套件过新或过旧
小程序合法域名失败 非 443 默认、TLS 不合规、证书链问题、域名未登记、业务未备案(政策以平台文档为准)

8. 综合示例:本地证书与 Spring Boot 💻

8.1 最简自签(仅本机,带 SAN 更好)

/etc/hosts 或 Windows hostslocal.test 指到 127.0.0.1,用 SAN 覆盖该名字(OpenSSL 命令各版本用 req v3_req + subjectAltName 配置块;此处给思路,命令以你本机 OpenSSL 手册为准):

  • 生成 私钥 + CSR + 自签,SAN 含 DNS:local.testDNS:localhost
  • Spring:server.ssl.certificate / certificate-private-key 指向生成文件,server.port=8443
  • 浏览器首次仍会红屏——导入自签为受信任根或每次点高级继续,团队规范通常禁止把这种证书用于联调小程序。

8.2 开发利器 mkcert(可选)

mkcert 在开发机上生成本地受信 CA,浏览器少红屏;仍非公网方案,上线与小程序无关。

8.3 「与生产一致」的路径

尽量用测试环境已申请的正式域+正式证书(或 staging),连接到类生产网关,减少「本地自签一切正常、上云全挂」的落差。


9. 双向 TLS(mTLS)——知道边界即可 🔗

部分 开放银行 / 企业 B2B 要求 客户端也交证书,服务端校验「你是谁」。此时除服务端 server.ssl.*(trust-store 配合作方根或伙伴客户端认证配置)外,客户端要配 keyStore(自己的证书+私钥)。细节强烈依赖对方接口文档与你们网关是否 offload,这里只提醒:遇到「要交客户端证书」的需求,别在业务代码里硬编码私钥路径密码,交给配置中心与密钥托管。


10. 小程序 / 开放平台的「合法域名」——和 HTTPS 的关系 📱

不同平台细则会变,开发时可按下面自查(以微信等官方文档为准,这里列工程常识):

  1. 接口域名必须是 HTTPS(小程序里 request 合法域名列表)。
  2. 证书链完整、TLS 版本与套件满足平台最低要求(旧版文档常写不支持 SSLv3 等,需读当期说明)。
  3. 域名已加到控制台「合法域名」,且与证书 SAN 一致
  4. 部分能力要求 ICP 备案业务域名校验文件等——与纯 TLS 不同,但排障时一起查。

「后端同事说接口OK,小程序不通」——先手机浏览器打开同一 URL 看证书;再用开发者工具 Network;最后核对控制后台域名配置


11. OpenSSL:带 SAN 的自签与 CSR 思路(可复制骨架)🧰

下面是一段「思路骨架」:用独立配置文件把 SAN 写清楚,避免证书只有 CN=localhost、没有 SAN,被 Chrome 等直接标红。具体命令以你机器上 OpenSSL 3.x / 1.1.1 文档为准,路径自行替换。

第一步:写 openssl-san.cnf(示例)

[ req ]
default_bits       = 2048
prompt             = no
default_md         = sha256
distinguished_name = dn
req_extensions     = v3_req

[ dn ]
CN = local.test

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = local.test
DNS.2 = localhost
IP.1  = 127.0.0.1

第二步:生成私钥 + CSR + 自签证书(示例命令序列)

openssl req -new -x509 -nodes -days 365 -keyout local.test.key -out local.test.crt -config openssl-san.cnf -extensions v3_req

生成的 local.test.crt + local.test.key 可交给 Spring Boot server.ssl.certificate / certificate-private-key(或先拼成 p12 再用 key-store)。记得hosts 里把 local.test 指到本机,否则浏览器用别的名字访问仍会报域名不匹配。

验证书内容(看 SAN 是否在):

openssl x509 -in local.test.crt -noout -text | findstr /i "Subject Alternative Name DNS"

Linux / macOS 把 findstr 换成 grep -A1 'Subject Alternative Name' 即可。


12. Let’s Encrypt / 自动化续期(工程侧常识)📅

公网免费证书里 ACME(如 Let’s Encrypt)最普及。工程上你要记住:

  1. 有效期短(常见 90 天),必须 自动续期(certbot、caddy、云厂商托管证书等),不能靠人肉日历。
  2. 验证域名所有权常见 HTTP-01(临时文件或路由)、DNS-01(改 TXT 记录)。在 K8s/多副本 上要用平台认可的 controller,避免验证打到错误 Pod。
  3. 证书更新后,Nginx 要 reload、部分 Java 进程要 热加载或滚动发布——别把「续期成功」当成「应用已用上新证」。
  4. 完整链:ACME 客户端一般会给你 fullchain.pem,部署时用它,而不是只拿单张 cert.pem

你只写 Java 时,续期多在运维;但事故复盘里「昨晚续期、今早握手失败」要能参与对话。


13. Nginx 上再多了几招(安全 баз线 + mTLS 提示)🛡

13.1 安全基线(示意,具体以你们等保/基线为准)

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# 若仍要 TLSv1.2,可配 ssl_ciphers ... 由安全组给清单;TLSv1.3 下多由 OpenSSL 默认择优

OCSP Stapling(减少客户端延迟、隐私略好)在 Nginx 有 ssl_stapling on; 等指令,但要链与解析都正确,配不好不如关——交给运维评估。

HSTS(示意):

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

首次上线要确认没有依赖明文 HTTP 的用户书签,否则最长一年内难撤回影响;includeSubDomains 影响子域,慎开。

13.2 mTLS 在 Nginx 边缘做校验(概念)

ssl_client_certificate /etc/nginx/certs/ca-chain.pem;
ssl_verify_client optional;  # 或 require,视业务

通过后再把必要信息 proxy_set_header 带给后端(如证书序列号需在网关文档化,别直接传整 PEM 进 HTTP 头)。Spring 若不做 mTLS,就信任已鉴权的内网回源。


下表是「认得名字、会搜文档」用,精确键名以官方文档为准(Minor 升级会补属性)。

场景 常见配置方向
PKCS12 路径 server.ssl.key-store(或文档中的 bundle 写法)
密钥库密码 server.ssl.key-store-password(环境变量注入)
密钥口令不同 server.ssl.key-password
PEM 证书 server.ssl.certificate(或 certificate-chain
PEM 私钥 server.ssl.certificate-private-key
禁用旧协议 server.ssl.enabled-protocols
限制套件 server.ssl.ciphers
只信任客户端证 server.ssl.client-auth(如需 mTLS)
识别反代 HTTPS server.forward-headers-strategy=framework

双端口:有人喜欢 8080 仅内网、对外只暴露 8443 —— 安全组与防火墙要和架构一致,别只留下 8080 给公网。


15. JVM 与 Java 出站的调试开关(仅排障短暂开)🔧

握手调试(日志极多,仅本机或灰度短时间):

-Djavax.net.debug=ssl:handshake:verbose

你会看到 ClientHello、选中套件、证书链加载等;抓完记得关,避免磁盘爆掉。

信任库 / 密钥库

-Djavax.net.ssl.trustStore=C:\dev\corp-truststore.p12
-Djavax.net.ssl.trustStorePassword=secret
-Djavax.net.ssl.trustStoreType=PKCS12

误区别踩

  • trustStorekeyStore(客户端证书)混在一个文件里管理,权限难审计。
  • 生产关闭证书校验(TrustAll)方便「联调」,结果忘了关上线。
  • 只改 Nginx 证书却未 reload,导致新旧证并存、节点不一致。

16. 和【001】叠在一起:分层心智图 📌

以后排障可以在心里按层掰:

[ 浏览器 / 小程序 / Postman ]
        │  DNS(【003】会讲)
        ▼
[ TCP 连接 ]
        │  TLS 握手(本篇)
        ▼
[ HTTP/1.1 或 HTTP/2 上的请求/响应 ]  ← 【001】
        ▼
[ 反代 / 网关 / Spring MVC / 业务 ]

现象归类:锁形报错、握手失败 → 先停在本篇;已经 200/401/502 → 回到【001】与业务日志。


17. 常见误区十条(口试 / 复盘可向团队念)📋

  1. 「HTTPS 就绝对安全」—— TLS 只保传输,鉴权弱、SQL 注入、账号泄露仍会发生。
  2. 「自签证书和 CA 证书只是心理安慰」——差在 公共信任根,小程序/公网用户不认自签。
  3. 「证书在 Nginx 更新了,Java 不用动」——若 Java 直连 旧入口或缓存了 OCSP/会话,要分路径讨论。
  4. 「PEM 好几个文件随便拼」——顺序错了链就断。
  5. 「禁 TLSv1.0 会掉所有用户」—— 2020 年代基本该禁,但要留 监控 看旧客户端占比。
  6. 「502 一定是 Java 挂了」—— SSL 在网关花式配置也会导致 握手失败或协议错误
  7. 「开发关校验、生产复制同一镜像」—— 不要把 dev 的 trust 策略带进交付物。
  8. 「Cookie 不设 Secure 也能混 HTTPS」——现代浏览器对混用越来越严,按规范设。
  9. 「443 以外也能叫 HTTPS」——可以,但小程序/CDN 默认策略常卡 443,先读平台文档。
  10. 「证书没过期就没事」——还有 算法迁移、根证淘汰(历史上有)之类「日历外」事件,跟运维订阅公告。

小结 💡

  • HTTPS 仍是【001】的 HTTP,只是先过 TLS:握手失败则业务层无感、日志可能只有 SSL 异常。
  • 证书看 SAN、有效期、链完整fullchain 是运维口头禅也是有技术缘由的。
  • TLS 1.3 更快更严;SNI 决定「同一 IP 上找对虚拟主机证书」。
  • Spring Boot 可用 PKCS12/PEM 直开 TLS,但更多生产是 边缘终结 + Forwarded 头 + Boot 认反代
  • Java 客户端牢记 cacerts / 自定义 truststorePKIX 系列错误。
  • 排障:curl -vopenssl s_client、浏览器证书详情三连,再分「握手 vs HTTP」。

下一篇(003)预告 🌐:TCP/IP 分层、超时与「后端接口为什么慢 / 为什么断」——和网关、Feign、数据库、线程池超时怎么对照着看。