大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
加密与 TLS:从哈希到证书链
这一课你将能做到
- 区分对称加密、非对称加密、哈希的适用场景,说清楚口令为什么要用 BCrypt
- 用 openssl 生成密钥与证书、验证一条真实站点的证书链
- 给 Spring Boot 配上 HTTPS,并按检查项评估自己平台的 TLS 配置
为什么要学
TLS 是现代互联网的信任基石,但你每天配的 server.ssl.* 背后发生了什么?面试里「HTTPS 为什么安全」几乎必问;实战里证书链配错、密码套件太弱、口令存明文,都是高频事故。不懂加密,你看不出 JWT 弱密钥的危害,也看不出为什么 MD5 存密码等于裸奔。
人话:哈希是「榨汁机」(不可还原),对称加密是「保险箱+同一把钥匙」,非对称加密是「信箱」(公开地址投递、私钥才能取)。
先建立图像
TLS 握手中的角色分工:
客户端 服务器
│ ── ClientHello(支持的套件+随机数) ──▶│
│◀─ ServerHello + 证书链 ─────────────│ ①证书链验证:根CA→中间CA→站点证书
│ ◀─ 用证书里的公钥协商会话密钥 ────────│ ②非对称加密只用于握手
│ ════ 之后全部用会话密钥对称加密 ═════▶│ ③业务数据走对称加密(快)
三类工具各司其职:
| 工具 | 可逆? | 用途 | 例子 |
|---|---|---|---|
| 哈希(SHA-256) | 不可逆 | 完整性校验、口令存储 | 文件校验、BCrypt 存密码 |
| 对称(AES) | 可逆,同钥 | 大量数据加密 | TLS 会话数据、磁盘加密 |
| 非对称(RSA/Ed25519) | 可逆,公私钥对 | 密钥交换、签名 | TLS 握手、JWT 签名 |
跟着做
步骤 1:生成密钥对与自签证书
# 生成 EC 私钥 + 自签证书(有效期 365 天,仅测试用)
openssl ecparam -genkey -name prime256v1 -out server.key
openssl req -new -x509 -key server.key -out server.crt \
-days 365 -subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
步骤 2:本地验证证书在服务端可用
# 用 openssl 起一个 TLS 服务(自建环境演示)
openssl s_server -port 8443 -key server.key -cert server.crt -www &
# 客户端连接(-CAfile 指向自签证书,自签链需要手动信任)
openssl s_client -connect localhost:8443 -CAfile server.crt
# 观察输出:Verify return code: 0 (ok)
步骤 3:验证真实站点证书链
openssl s_client -connect httpbin.org:443 -showcerts
# 看 Certificate chain 段:0 站点证书 → 1 中间CA → (根CA预置在系统)
# Verify return code: 0 (ok) 表示链完整可信
# 不加 -CAfile 的自签站点会报 self signed certificate
步骤 4:检查 TLS 配置强弱
# 安装 testssl.sh 检查自建靶机的 TLS
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
./testssl.sh/testssl.sh https://localhost
# 关注:协议版本(TLS1.0/1.1 应禁用)、弱套件、证书链完整性
步骤 5:口令存储实验(哈希必须加盐慢哈希)
# MD5/SHA 之所以不适合存口令:太快,GPU 每秒可算百亿次
echo -n "password123" | md5sum # 瞬间完成
# Java 里正确做法(Spring Security 已内置):
// Spring Security 的口令编码器:BCrypt 自带盐、故意慢
PasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hash = encoder.encode("password123");
boolean ok = encoder.matches("password123", hash);
步骤 6:Spring Boot 配 HTTPS
# 把自签证书转成 PKCS12 给 Spring Boot 用
openssl pkcs12 -export -in server.crt -inkey server.key \
-out server.p12 -name server
# application.yml
server:
ssl:
key-store: classpath:server.p12
key-store-password: ${SSL_KEYSTORE_PWD} # 从环境变量注入
key-store-type: PKCS12
port: 8443
讲清楚要点
- 哈希不是加密:哈希不可逆,用于验证「内容相同」;加密可逆,用于保护「内容保密」。把「加密密码」说成哈希校验是面试常见错误。
- 口令存储三要素:加盐(防彩虹表)、慢函数(防暴力枚举)、可调成本因子(硬件进步时能升级)。BCrypt/Argon2/scrypt 都满足,MD5/SHA1/SHA256 直接存都不满足。
- 证书链验证逻辑:站点证书由中间 CA 签发,中间 CA 由根 CA 签发,根 CA 预置在操作系统信任库。缺中间证书是最常见部署事故——浏览器有的能补、curl 直接报错。
- 非对称加密不做业务加密:RSA 慢且有长度限制,TLS 只用它在握手阶段交换会话密钥,之后全走 AES 这类对称加密。
- 签名与加密是两件事:签名证明「谁发的、没被改」(用私钥签、公钥验);加密保证「只有收件人能读」。JWT 签名不加密——payload 默认 base64 可读。
你可能会懵的地方
-
HTTPS 抓包就完全看不到了吗? 不配密钥确实只能看到元数据。所以中间人攻击的核心是伪造证书:除非攻击者能把根证书装进你的信任库,否则链验证会失败。这就是公共 Wi-Fi 上仍建议全程 HTTPS 的原因。
-
自签证书安全吗? 加密强度没问题,问题是「身份不可信」——任何人都能给自己签一张 localhost。公网服务必须用受信 CA 签发的证书,Let's Encrypt 免费解决这件事。
-
为什么 BCrypt 的盐不用保密? 盐的作用是让相同口令产生不同哈希、让彩虹表失效,它随哈希一起存储,本身不需要保密。需要保密的是口令本身。
-
TLS 1.3 比 1.2 好在哪? 握手往返更少(1-RTT)、废除了 RSA 密钥传输改为前向安全的密钥交换(ECDHE)、砍掉全部弱套件。新部署应优先 1.3,至少保留 1.2 并禁用 1.0/1.1。
-
JWT 用 HS256 还是 RS256? HS256 共享密钥,适合单体应用;RS256/EdDSA 私钥签名、公钥验签,适合多服务:验证方拿不到私钥,被攻破一个服务也伪造不了 token。本站平台多模块场景建议非对称。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 密码用 MD5/SHA256 直接存 | 速度太快,撞库秒破 | BCrypt/Argon2 慢哈希 + 盐 |
| 自签证书上生产公网站点 | 用户全屏告警,无法防钓鱼 | Let's Encrypt 等受信 CA |
| 密钥硬编码在代码/配置里 | 提交仓库即泄露 | 环境变量 / KMS / Vault |
| 只配证书不验证证书链 | 部分客户端握手失败 | openssl s_client 验证 Verify code=0 |
| 用 RSA 直接加密大文件 | 长度限制且极慢 | AES 加数据 + RSA 保护会话密钥 |
卡点急救
- 浏览器提示证书不受信任(自签):测试环境把自签 CA 导入系统信任库;生产直接换 Let's Encrypt(certbot 一条命令签发续期)。
- curl 报 unable to get local issuer certificate:服务端漏了中间证书。用
openssl s_client -showcerts看链是否完整,把中间证书拼进 fullchain。 - Java 启动报 Invalid keystore format:JDK 版本与 PKCS12 参数不匹配,重新
openssl pkcs12 -export -legacy或指定-keypbe兼容算法;确认key-store-type与文件一致。
面试常问
-
HTTPS 握手过程?为什么非对称加密只用在握手? ClientHello(套件+随机数)→ 服务器返回证书链 → 客户端验证链并用公钥协商会话密钥(TLS1.3 走 ECDHE 前向安全)→ 之后用会话密钥对称加密。非对称慢且 RSA 加密无前向安全,只适合短时的密钥协商;对称快,适合业务数据。
-
为什么密码要 BCrypt 而不是 SHA256 加盐? SHA256 计算速度对 GPU 极快,即使加盐也能高速离线爆破;BCrypt/Argon2 故意慢、支持成本因子,且 BCrypt 内置盐管理。升级成本因子还能对抗硬件进步。
-
证书链是怎么验证的?中间证书缺失会怎样? 从站点证书逐级向上找签发者,最终锚定到系统信任库里的根 CA,同时校验有效期、域名 SAN、吊销状态。缺中间证书时部分浏览器能 AIA 补链,curl/Java 默认直接握手失败。
自测三问
- 对称加密、非对称加密、哈希在 TLS 里各用在哪一步?
- 为什么说「MD5 存密码加盐」也不够?
- Spring Boot 配置 HTTPS 需要哪几项?私钥口令应该放哪?
小结一句话
哈希保完整、对称保效率、非对称保信任:TLS 用证书链建立身份,用握手换来会话密钥,而你的口令必须交给慢哈希保管。
延伸
- Mozilla TLS 配置生成器
- RFC 8446: TLS 1.3
- 下一课:认证与授权——JWT、OAuth2 与 SSO 的原理与攻防。