Files
2026-05-24 11:42:38 +08:00

6.1 KiB
Raw Permalink Blame History

tags, create time
tags create time
计算机网络
HTTPS
TLS
证书
PKI
2026-05-18 03:00

HTTPS 与 TLS 握手

概述

HTTPS = HTTP over TLS(Transport Layer Security)。TLS 在 TCP 和应用层之间插入一个加密通道,确保通信的机密性、完整性和对端身份验证。理解 TLS 握手过程是排查 SSL 错误的核心能力。

TLS 在协议栈中的位置

┌─────────────┐
│    HTTP     │  ← 应用层
├─────────────┤
│     TLS     │  ← "加密壳" — 透明包装下层数据
├─────────────┤
│     TCP     │  ← 可靠传输
├─────────────┤
│     IP      │  ← 路由
└─────────────┘

TLS 握手流程(TLS 1.3)

完整交互序列

sequenceDiagram
    participant C as Client
    participant S as Server
    
    Note over C,S: Phase 1: Key Exchange (Full Handshake)
    C->>S: ClientHello<br/>• TLS version (1.3)<br/>• Cipher suites (pref order)<br/>• Random bytes<br/>• PSK/session ticket (optional)<br/>• Extensions (ALPN, SNI...)
    
    S-->>C: ServerHello<br/>• Selected cipher suite<br/>• Random bytes<br/>• Server's key share (ephemeral ECDHE)
    
    S->>S: Send certificate + cert_chain
    S->>C: CertificateRequest (optional)
    S->>C: ServerKeyExchange (if needed)
    S->>C: ServerFinished
    
    C->>C: Verify cert chain → CA ✓
    C->>S: ClientKeyExchange (key share)
    C->>C: Compute shared secret (ECDHE)
    C->>S: ChangeCipherSpec (implicit in 1.3)
    C->>S: ClientFinished
    
    Note over C,S: ← Encryption Enabled ✨ -->
    
    C->>S: Application data (HTTP Request) 🔒
    S->>C: Application data (HTTP Response) 🔒

TLS 1.3 vs TLS 1.2 对比

特性 TLS 1.2 TLS 1.3
往返次数 2 RTT (或 1 RTT with session resumption) 1 RTT (0 RTT if resumed) ⚡
密钥交换 RSA / DHE / ECDHE 仅 ECDHE (前向安全强制)
Cipher Suites ~30+ 种选择 仅 4 种 (AES-GCM, ChaCha20-Poly1305)
压缩 ✅ (导致 CRIME 漏洞) ❌ 已禁用
重协商 ✅ ❌ (改用 NewSession Ticket)
静态 RSA 密钥交换 ✅ ❌ 已移除
CBC 模式密文 ✅ ❌ 已移除
Export ciphers ✅ ❌ 已移除
Renegotiation ✅ ❌ (用 renegotiation_info 扩展替代)

[!tip] TLS 1.3 为什么更快? TLS 1.2: ClientHello → ServerHello → Certificate → ServerKeyExchange → ... → ClientKeyExchange → Finished = 2 full RTTs

TLS 1.3: ClientHello (含 client key share) → ServerHello (含 server key share) + Certificate + Finished = 1 RTT

因为客户端在第一个包中就携带了自己的密钥共享信息,无需等待服务器回复后再计算。

Cipher Suite 详解

TLS 1.3 只有 4 个 Cipher Suites:

编号 Cipher Suite KEM AEAD HKDF
TLS_AES_128_GCM_SHA256 AES-128-GCM X25519 AES-128-GCM SHA256
TLS_AES_256_GCM_SHA384 AES-256-GCM X25519 AES-256-GCM SHA384
TLS_CHACHA20_POLY1305_SHA256 ChaCha20-Poly1305 X25519 ChaCha20-Poly1305 SHA256
TLS_AES_128_CCM_SHA256 AES-128-CCM X25519 AES-128-CCM SHA256

Go 中默认偏好顺序: TLS_AES_256_GCM_SHA384 > TLS_AES_128_GCM_SHA256 > TLS_CHACHA20_POLY1305_SHA256

证书链验证流程

flowchart TD
    Cert["Server Certificate<br/>CN = example.com"] -->|"issued by"| Inter["Intermediate CA<br/>ISRG Root X1"]
    Inter -->|"issued by"| Root["Root CA<br/>DigiCert Global Root G2"]
    Root -->|"self-signed"| Root
    
    Client["Client 浏览器"] -->|"验证:"| V1["1. 签名是否由上层CA签发?"]
    V1 -->|"是"| V2["2. CN/SAN 是否匹配域名?"]
    V2 -->|"是"| V3["3. 是否过期?"]
    V3 -->|"是"| V4["4. 是否在 OCSP/CRL 吊销列表中?"]
    V4 -->|"是"| V5["5. 根证书是否在信任库中?"]
    V5 -->|"是"| TRUSTED["✅ 信任!"]
    
    style TRUSTED fill:#98FB98,color:#000

实际调试命令

# 查看服务器支持的 TLS 版本和 Cipher Suites
$ openssl s_client -connect example.com:443 -tls1_3
CONNECTED(00000003)
Protocol  : TLSv1.3
Cipher    : TLS_AES_256_GCM_SHA384
Certificate chain:
 0 s:CN = example.com
   i:C = US, O = DigiCert Inc, CN = DigiCert TLS Hybrid ECC_SHA384 2020 CA1

# 指定特定 Cipher 测试
$ openssl s_client -connect example.com:443 \
    -cipher 'ECDHE-ECDSA-AES256-GCM-SHA384'

# 获取证书信息
$ openssl x509 -in cert.pem -noout -dates -subject -issuer
  subject=CN = example.com
  notBefore=Jan  1 00:00:00 2025 GMT
  notAfter=Jan  1 00:00:00 2026 GMT

# 检查 OCSP 状态
$ curl -vI https://example.com
  < HTTP/2 200
  < alt-s: h3=":443"; ma=86400
  < alt-s: h2=":443"; ma=86400

# Go 中自定义证书验证
import "crypto/tls"
config := &tls.Config{
    InsecureSkipVerify: false,  // 生产环境永远为 false
    MinVersion:         tls.VersionTLS13,
}
conn, _ := tls.Dial("tcp", "example.com:443", config)

Go 标准库中的 TLS

// 创建安全的 TLS 配置
var DefaultTLSConfig = tls.Config{
    MinVersion: tls.VersionTLS12, // 最低支持 TLS 1.2
    PreferServerCipherSuites: true,
}

// http.Server 自动启用 TLS
srv := &http.Server{
    Addr:    ":443",
    Handler: handler,
    TLSConfig: &tls.Config{
        MinVersion: tls.VersionTLS13,
        NextProtos: []string{"h2", "http/1.1"}, // ALPN
    },
}
srv.ListenAndServeTLS("cert.pem", "key.pem")

// 或 Let's Encrypt (auto TLS)
import "crypto/tls"
mux.HandleFunc("/", handler)
go http.ListenAndServe(":80", nil) // ACME Challenge
go http.ListenAndServeTLS(":443", "", "", &http.Server{
    Handler: mux,
    TLSConfig: &tls.Config{
        GetCertificate: autoTLSManager.GetCertificate,
        NextProtos:     []string{"h2", "http/1.1"},
    },
})

关联笔记