实现一个手写的 TLS 1.2 客户端

从来不信什么一夜成名,一夜暴富,只信一分耕耘一分收获。 如果有一天,你的努力配得上你的梦想,那么你的梦想也绝对不会辜负你的努力

Posted by yishuifengxiao on 2026-08-09

基础函数

// prf12 实现 TLS 1.2 的伪随机函数 PRF(secret, label, seed)
// 根据 RFC 5246 Section 5 定义:PRF(secret, label, seed) = P_<hash>(secret, label + seed)
// 其中 P_hash 基于 HMAC 的迭代构造
// 参数说明:
// - secret: 共享密钥(预主密钥或主密钥)
// - label: 标签字符串(如 "master secret", "key expansion")
// - seedA, seedB: 种子分量(通常为客户端随机数和服务器随机数)
// - outLen: 期望输出的字节长度
// - useSHA384: 是否使用 SHA-384(对应 AES-256 套件),否则使用 SHA-256
func prf12(secret, label, seedA, seedB []byte, outLen int, useSHA384 bool) []byte {
// 将两个种子分量拼接成完整种子
seed := append(seedA, seedB...)
// 将标签与种子拼接,形成 PRF 的输入数据
labelSeed := append(label, seed...)
// 根据 useSHA384 选择哈希函数
h := sha256.New
if useSHA384 {
h = sha512.New384
}
// 调用 P_hash 生成指定长度的伪随机输出
return pHash(secret, labelSeed, outLen, h)
}

// pHash 实现 TLS 基于 HMAC 的 P_hash 扩展函数
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + HMAC_hash(secret, A(2) + seed) + ...
// 其中 A(0) = seed, A(i) = HMAC_hash(secret, A(i-1))
// 这是 TLS 密钥派生的核心构造,用于生成主密钥和密钥块
func pHash(secret, seed []byte, outLen int, h func() hash.Hash) []byte {
// 预分配结果缓冲区
result := make([]byte, 0, outLen)
// A 为 HMAC 的迭代输入,初始为 seed
A := seed
// 循环生成直到输出长度满足要求
for len(result) < outLen {
// 计算 A(i) = HMAC_hash(secret, A(i-1))
A = hmacHash(secret, A, h)
// 计算 HMAC_hash(secret, A(i) + seed) 并追加到结果
result = append(result, hmacHash(secret, append(A, seed...), h)...)
}
// 截断到指定长度
return result[:outLen]
}

// hmacHash 使用指定的哈希函数计算 HMAC
// 这是 TLS PRF 构造的基础密码学原语
func hmacHash(secret, data []byte, h func() hash.Hash) []byte {
// 创建 HMAC 实例,使用 secret 作为密钥
mac := hmac.New(h, secret)
// 写入要签名的数据
mac.Write(data)
// 计算并返回 HMAC 值
return mac.Sum(nil)
}

新密钥派生

使用客户端私钥和服务器公钥计算共享密钥(预主密钥)

// 使用客户端私钥和服务器公钥计算共享密钥(预主密钥)
sharedSecret, err := computeSharedSecretECDH(st.skxCurve, priv, st.skxServerPub)

预主密钥sharedSecret即为preMaster

// deriveKeysAEAD 使用 TLS PRF 派生 AEAD 密码套件所需的密钥
// 密钥派生过程(RFC 5246 Section 6.3):
//
// master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) [0..47]
// key_block = PRF(master_secret, "key expansion", ServerHello.random + ClientHello.random) [0..key_len*2+7]
// client_write_key = key_block[0..key_len-1]
// server_write_key = key_block[key_len..2*key_len-1]
// client_write_IV = key_block[2*key_len..2*key_len+3]
// server_write_IV = key_block[2*key_len+4..2*key_len+7]
//
// 对于 GCM 模式,IV 为 4 字节(隐式部分),实际 nonce = IV(4字节) || explicit_nonce(8字节,通常为序列号)
func deriveKeysAEAD(preMaster, clientRand, serverRand []byte, keyLen int, useSHA384 bool) ([]byte, []byte, []byte, []byte) {
// 从预主密钥派生主密钥(48字节)
masterSecret := prf12(preMaster, []byte("master secret"), clientRand, serverRand, 48, useSHA384)
fmt.Printf(" masterSecret=%x\n", masterSecret)

// 从主密钥派生密钥块
// key_block 长度 = key_len*2(两个写密钥)+ 4*2(两个 IV,各4字节)
keyBlock := prf12(masterSecret, []byte("key expansion"), serverRand, clientRand, keyLen*2+8, useSHA384)
clientWriteKey := keyBlock[0:keyLen]
serverWriteKey := keyBlock[keyLen : keyLen*2]
clientWriteIV := keyBlock[keyLen*2 : keyLen*2+4]
serverWriteIV := keyBlock[keyLen*2+4 : keyLen*2+8]
fmt.Printf(" clientWriteKey=%x\n", clientWriteKey)
fmt.Printf(" serverWriteKey=%x\n", serverWriteKey)
fmt.Printf(" clientWriteIV=%x\n", clientWriteIV)
fmt.Printf(" serverWriteIV=%x\n", serverWriteIV)

return clientWriteKey, serverWriteKey, clientWriteIV, serverWriteIV
}

密钥派生是两阶段过程:

阶段一:从预主密钥派生出主密钥

master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) [0..47]
  • 输入:ECDHE 计算的共享密钥 (sharedSecret)、标签 "master secret"、客户端随机数 + 服务端随机数
  • 调用 prf12生成 48 字节主密钥
  • prf12 内部使用 pHash 实现 TLS 的 P_hash 扩展函数,基于 HMAC 迭代生成任意长度的伪随机输出

阶段二:从主密钥派生出密钥块

key_block = PRF(master_secret, "key expansion", ServerHello.random + ClientHello.random) [0..key_len*2+7]
  • 输入:master_secret、标签 "key expansion"服务端随机数 + 客户端随机数(注意顺序与阶段一相反)
  • 输出长度 = keyLen * 2 + 8,其中:
    • keyLen 根据密码套件确定:AES-128 为 16 字节,AES-256 为 32 字节
    • 额外 8 字节是两个 IV(各 4 字节)

密钥拆分

key_block[0..keyLen-1]              → client_write_key
key_block[keyLen..2*keyLen-1] → server_write_key
key_block[2*keyLen..2*keyLen+3] → client_write_IV
key_block[2*keyLen+4..2*keyLen+7] → server_write_IV
  • client_write_key/IV:客户端加密发送数据、服务端解密接收数据
  • server_write_key/IV:服务端加密、客户端解密

Finished 验证数据计算

计算握手消息哈希

serverHsMsgs := extractHandshakeMessages(serverData)        // 提取服务端握手消息体
allHsMsgs := append(clientHelloHsMsg, serverHsMsgs...) // 拼接 ClientHello + 服务端消息
allHsMsgs = append(allHsMsgs, ckeHsMsg...) // 拼接 ClientKeyExchange

关键点:

  • extractHandshakeMessages从 TLS 记录层中剥离记录层头(5 字节),只提取握手消息负载
  • 只包含类型为 0x16(Handshake)的记录
  • 不包含 ChangeCipherSpec(它是记录层协议,不是握手协议消息)

然后根据密码套件选择哈希算法:

  • SHA-256:AES128-GCM-SHA256 套件
  • SHA-384:AES256-GCM-SHA384 套件
// extractHandshakeMessages 从 TLS 记录层数据中提取所有握手消息的原始字节
// 用于计算 Finished 消息的哈希值
// Finished 消息需要对所有之前发送和接收的握手消息内容进行哈希
func extractHandshakeMessages(tlsRecords []byte) []byte {
var handshakeMsgs []byte
r := bytes.NewReader(tlsRecords)
for r.Len() >= 5 {
var typ [1]byte
if _, err := r.Read(typ[:]); err != nil {
break
}
var ver [2]byte
if _, err := r.Read(ver[:]); err != nil {
break
}
var length uint16
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
break
}
payload := make([]byte, length)
if _, err := io.ReadFull(r, payload); err != nil {
break
}
// 仅提取 Handshake 类型的记录(0x16),忽略 Alert 等其他类型
if typ[0] == 0x16 {
handshakeMsgs = append(handshakeMsgs, payload...)
}
}
return handshakeMsgs
}

2.2 计算 verify_data

verify_data = PRF(master_secret, "client finished", Hash(handshake_messages)) [0..11]
  • 输入:master_secret、标签 "client finished"、握手消息哈希值
  • 输出固定为 12 字节(TLS 1.2 规范要求)
  • 这是客户端证明自己拥有正确主密钥的凭证

发送 ChangeCipherSpec

在发送 Finished 之前,必须先发送 ChangeCipherSpec:

  • 内容固定为 0x14, 0x03, 0x03, 0x00, 0x01, 0x01
  • 始终以明文传输
  • 发送后序列号重置为 0st.clientSeq = 0),因为新 epoch 开始

加密并发送 Finished 消息

构建 Finished 握手消息

finishedMsg: 0x14 || 0x00000C || verify_data(12字节)
  • 握手类型 0x14 = Finished
  • 长度 0x00000C = 12 字节
  • 内容为 12 字节 verify_data
if useSHA384 {
h := sha512.Sum384(allHsMsgs)
hsHash = h[:]
fmt.Printf(" Handshake messages SHA-384 hash=%x\n", hsHash)
} else {
h := sha256.Sum256(allHsMsgs)
hsHash = h[:]
fmt.Printf(" Handshake messages SHA-256 hash=%x\n", hsHash)
}

// 计算 Finished 验证数据
// verify_data = PRF(master_secret, "client finished", Hash(handshake_messages))
verifyData := prf12(st.masterSecret, []byte("client finished"), hsHash, nil, 12, useSHA384)

使用新密钥加密

调用 encryptGCM

nonce = client_write_IV(4字节) || 序列号(8字节)
AAD = 序列号(8字节) || 内容类型(0x16) || 版本(0x0303) || 明文长度(2字节)
输出 = explicit_nonce(8字节, 即序列号) || GCM密文 || GCM认证标签(16字节)

关键参数:

  • 写密钥st.clientWriteKey
  • 隐式 IVst.clientWriteIV(4 字节,来自密钥块)
  • 序列号st.clientSeq(ChangeCipherSpec 后已重置为 0)
  • 内容类型0x16(Handshake),GCM 将内容类型纳入 AAD 进行完整性保护
// 构建 Finished 握手消息:类型(0x14) + 长度(0x00000C) + verify_data
finishedMsg := bytes.NewBuffer(nil)
finishedMsg.WriteByte(0x14)
finishedMsg.Write([]byte{0x00, 0x00, 0x0C})
finishedMsg.Write(verifyData)
fmt.Printf(" finishedMsg (%d bytes): %x\n", finishedMsg.Len(), finishedMsg.Bytes())

// 使用新密钥加密 Finished 消息后发送
plainFinished := finishedMsg.Bytes()
encryptedFinished := encryptGCM(plainFinished, st.clientWriteKey, st.clientWriteIV, st.clientSeq, 0x16)

构建 TLS 记录层

TLS记录层: 0x16 || 0x0303 || 长度(2字节) || encryptedFinished
  • 外层记录层类型也是 0x16(Handshake)
  • 版本 0x0303(TLS 1.2)
  • 这里的记录层类型 0x16 与 GCM 加密 AAD 中的 0x16 一致

发送

发送后 st.clientSeq++,序列号变为 1。


接收服务端 Finished

服务端返回的 Finished 流程对称:

  1. 先收到明文 ChangeCipherSpec(0x14),服务端序列号重置为 0
  2. 使用 st.serverWriteKeyst.serverWriteIV 通过 decryptGCM 解密服务端 Finished
  3. 服务端的 verify_data = PRF(master_secret, "server finished", Hash(handshake_messages)),标签为 "server finished" 而非 "client finished"

总结流程图

预主密钥(sharedSecret)

▼ PRF("master secret", clientRand + serverRand)
master_secret (48字节)

▼ PRF("key expansion", serverRand + clientRand)
key_block (keyLen*2+8字节)

├── client_write_key (keyLen)
├── server_write_key (keyLen)
├── client_write_IV (4字节)
└── server_write_IV (4字节)

握手消息哈希 = Hash(ClientHello || ServerHello||Certificate||ServerKeyExchange||ServerHelloDone||ClientKeyExchange)

▼ PRF(master_secret, "client finished", hash)
verify_data (12字节) → Finished消息 → GCM加密 → 发送

完整代码

package main

import (
// bytes 提供字节缓冲区操作,用于构建 TLS 消息
"bytes"
// crypto 定义标准的密码算法参数(如 crypto.SHA256)
"crypto"
// crypto/aes 实现 AES 块加密算法,用于 GCM 模式的对称加密
"crypto/aes"
// crypto/cipher 提供密码学操作模式(如 GCM)的封装
"crypto/cipher"
// crypto/ecdh 实现椭圆曲线 Diffie-Hellman 密钥交换
"crypto/ecdh"
// crypto/ecdsa 实现椭圆曲线数字签名算法
"crypto/ecdsa"
// crypto/hmac 实现 HMAC 消息认证码,TLS PRF 的基础
"crypto/hmac"
// crypto/rand 提供密码学安全的随机数生成器
"crypto/rand"
// crypto/rsa 实现 RSA 加解密和签名验证
"crypto/rsa"
// crypto/sha256 实现 SHA-256 哈希算法
"crypto/sha256"
// crypto/sha512 实现 SHA-384 和 SHA-512 哈希算法
"crypto/sha512"
// crypto/x509 实现 X.509 证书解析
"crypto/x509"
// encoding/asn1 处理 ASN.1 DER 编码/解码(解析 ECDSA 签名)
"encoding/asn1"
// encoding/binary 提供大端序整数编解码
"encoding/binary"
// encoding/hex 提供十六进制编解码,用于调试输出

// fmt 提供格式化输出
"fmt"
// hash 定义通用哈希接口类型
"hash"
// io 提供 IO 接口,用于读取网络数据
"io"
// math/big 提供大整数类型(用于 ECDSA 签名的 R,S 值)
"math/big"
// net 提供网络连接(TCP 拨号)
"net"
// strings 提供字符串操作,用于 dumpHex 的 ASCII 显示
"strings"
)

// 常量定义:TLS 密码套件、扩展字段、椭圆曲线、签名算法等
const (
// TLS_RSA_WITH_AES_128_CBC_SHA: 传统 RSA 密钥交换 + AES-128-CBC + SHA-256
// 0x002F 表示该密码套件在 TLS 注册表中的编号
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
// TLS_ECDHE_ECDSA_AES256_GCM_SHA384: ECDHE-ECDSA 密钥交换 + AES-256-GCM + SHA-384
// 0xC02C 是该套件的官方分配编号
TLS_ECDHE_ECDSA_AES256_GCM_SHA384 = 0xC02C
// TLS_ECDHE_ECDSA_AES128_GCM_SHA256: ECDHE-ECDSA 密钥交换 + AES-128-GCM + SHA-256
// 0xC02B 是该套件的官方分配编号
TLS_ECDHE_ECDSA_AES128_GCM_SHA256 = 0xC02B
// TLS_ECDHE_RSA_AES256_GCM_SHA384: ECDHE-RSA 密钥交换 + AES-256-GCM + SHA-384
// 使用 RSA 证书进行服务器身份认证,但密钥交换仍使用 ECDHE 前向保密
TLS_ECDHE_RSA_AES256_GCM_SHA384 = 0xC030
// TLS_ECDHE_RSA_AES128_GCM_SHA256: ECDHE-RSA 密钥交换 + AES-128-GCM + SHA-256
TLS_ECDHE_RSA_AES128_GCM_SHA256 = 0xC02F

// TLS 扩展字段类型编号(RFC 5246 Section 7.4.1.4)
// EXT_SERVER_NAME (0x0000): SNI 扩展,用于告知服务器请求的域名,支持虚拟主机
EXT_SERVER_NAME = 0x0000
// EXT_SIG_ALGORITHMS (0x000D): 签名算法扩展,告知服务器客户端支持的签名算法列表
EXT_SIG_ALGORITHMS = 0x000D
// EXT_SUPPORTED_GROUPS (0x000A): 支持的组扩展(TLS 1.3 中替代 elliptic_curves),此处用于兼容
EXT_SUPPORTED_GROUPS = 0x000A
// EXT_EC_POINT_FORMATS (0x000B): EC 点格式扩展,声明支持的椭圆曲线点编码格式
EXT_EC_POINT_FORMATS = 0x000B
// EXT_EXTENDED_MASTER_SECRET (0x0017): 扩展主密钥扩展,防止降级攻击
EXT_EXTENDED_MASTER_SECRET = 0x0017
// EXT_RENEGOTIATION_INFO (0xFF01): 安全重协商扩展,用于防止重协商攻击
EXT_RENEGOTIATION_INFO = 0xFF01

// 椭圆曲线编号(RFC 4492 和 RFC 7748)
// CURVE_X25519 (0x001D): X25519 曲线,提供 128 位安全强度的 ECDHE 密钥交换
CURVE_X25519 = 0x001D
// CURVE_SECP256R1 (0x0017): NIST P-256 曲线,提供 128 位安全强度
CURVE_SECP256R1 = 0x0017
// CURVE_SECP384R1 (0x0018): NIST P-384 曲线,提供 192 位安全强度
CURVE_SECP384R1 = 0x0018

// 哈希算法编号(TLS SignatureAndHashAlgorithm)
// HASH_SHA256 (0x04): SHA-256 哈希算法
HASH_SHA256 = 0x04
// HASH_SHA384 (0x05): SHA-384 哈希算法
HASH_SHA384 = 0x05
// SIG_ECDSA (0x03): ECDSA 签名算法
SIG_ECDSA = 0x03
// SIG_RSA (0x01): RSA 签名算法
SIG_RSA = 0x01
)

// tlsState 保存整个 TLS 握手和连接过程中的状态信息
// 包含握手阶段的随机数、密钥交换参数、派生密钥和序列号等
type tlsState struct {
// domain: 目标域名,用于 SNI 扩展和 HTTP Host 头
domain string
// clientRand: 客户端随机值,由 ClientHello 生成,参与主密钥派生
clientRand []byte
// serverRand: 服务器随机值,从 ServerHello 获取,参与主密钥派生
serverRand []byte
// cipherSuite: 协商后的密码套件编号
cipherSuite uint16
// certDER: 服务器证书的 DER 编码,用于签名验证
certDER []byte
// serverPubKey: 服务器公钥(RSA 或 ECDSA),从证书解析得出
serverPubKey interface{}

// ====== ServerKeyExchange 相关字段 ======
// skxCurve: 服务器选择的椭圆曲线编号
skxCurve uint16
// skxServerPub: 服务器 ECDHE 公钥字节
skxServerPub []byte
// skxHashAlg: 服务器签名使用的哈希算法编号
skxHashAlg byte
// skxSigAlg: 服务器签名使用的签名算法编号
skxSigAlg byte

// ====== 客户端 ECDHE 密钥对 ======
// clientECPriv: 客户端生成的 ECDHE 私钥
clientECPriv *ecdh.PrivateKey
// clientECPub: 客户端生成的 ECDHE 公钥,发送给服务器
clientECPub *ecdh.PublicKey
// sharedSecret: ECDHE 计算出的共享密钥(预主密钥)
sharedSecret []byte

// ====== TLS 密钥派生结果 ======
// masterSecret: 主密钥,由预主密钥 + 随机数通过 PRF 派生
masterSecret []byte
// clientWriteKey: 客户端写密钥,用于加密客户端发送的数据
clientWriteKey []byte
// serverWriteKey: 服务器写密钥,用于加密服务器发送的数据
serverWriteKey []byte
// clientWriteIV: 客户端写初始向量(IV),用于 GCM 的 nonce 构造
clientWriteIV []byte
// serverWriteIV: 服务器写初始向量(IV),用于 GCM 的 nonce 构造
serverWriteIV []byte

// ====== 序列号,用于 GCM 的 nonce 和 AAD 构造 ======
// clientSeq: 客户端发送记录的序列号
clientSeq uint64
// serverSeq: 服务器发送记录的序列号
serverSeq uint64
}

// alertMsg 将 TLS Alert 描述码转换为可读字符串
// TLS Alert 协议定义了各种错误/警告的描述码(RFC 5246 Section 7.2)
func alertMsg(desc byte) string {
// 构建描述码到名称的映射表
m := map[byte]string{
0x00: "close_notify", // 关闭通知:连接正常关闭
0x0A: "unexpected_message", // 意外消息:收到意外的消息
0x14: "bad_record_mac", // 记录 MAC 错误:MAC 校验失败
0x15: "decryption_failed", // 解密失败:解密操作失败
0x16: "record_overflow", // 记录溢出:记录长度超限
0x17: "decompression_failure", // 解压失败
0x18: "handshake_failure", // 握手失败:协商失败
0x19: "no_certificate", // 无证书
0x1A: "bad_certificate", // 坏证书:证书不可接受
0x1B: "unsupported_certificate", // 不支持的证书
0x1C: "certificate_revoked", // 证书已撤销
0x1D: "certificate_expired", // 证书已过期
0x1E: "certificate_unknown", // 证书未知
0x1F: "illegal_parameter", // 非法参数:消息中参数不合法
0x20: "unknown_ca", // 未知 CA:不受信任的证书颁发机构
0x21: "access_denied", // 访问拒绝
0x22: "decode_error", // 解码错误:消息解码失败
0x23: "decrypt_error", // 解密错误:握手密码学操作失败
0x24: "export_restriction", // 出口限制
0x25: "protocol_version", // 协议版本不支持
0x26: "insufficient_security", // 安全不足
0x27: "internal_error", // 内部错误
0x28: "user_canceled", // 用户取消
0x29: "no_renegotiation", // 无重协商
0x2A: "unsupported_extension", // 不支持的扩展
0x2B: "certificate_unobtainable", // 证书不可获取
0x2C: "unrecognized_name", // 无法识别的名称
0x2D: "no_application_protocol", // 无应用协议
}
// 查找已知的描述码
if v, ok := m[desc]; ok {
return v
}
// 返回未知描述码的十六进制表示
return fmt.Sprintf("unknown(0x%02x)", desc)
}

// prf12 实现 TLS 1.2 的伪随机函数 PRF(secret, label, seed)
// 根据 RFC 5246 Section 5 定义:PRF(secret, label, seed) = P_<hash>(secret, label + seed)
// 其中 P_hash 基于 HMAC 的迭代构造
// 参数说明:
// - secret: 共享密钥(预主密钥或主密钥)
// - label: 标签字符串(如 "master secret", "key expansion")
// - seedA, seedB: 种子分量(通常为客户端随机数和服务器随机数)
// - outLen: 期望输出的字节长度
// - useSHA384: 是否使用 SHA-384(对应 AES-256 套件),否则使用 SHA-256
func prf12(secret, label, seedA, seedB []byte, outLen int, useSHA384 bool) []byte {
// 将两个种子分量拼接成完整种子
seed := append(seedA, seedB...)
// 将标签与种子拼接,形成 PRF 的输入数据
labelSeed := append(label, seed...)
// 根据 useSHA384 选择哈希函数
h := sha256.New
if useSHA384 {
h = sha512.New384
}
// 调用 P_hash 生成指定长度的伪随机输出
return pHash(secret, labelSeed, outLen, h)
}

// pHash 实现 TLS 基于 HMAC 的 P_hash 扩展函数
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + HMAC_hash(secret, A(2) + seed) + ...
// 其中 A(0) = seed, A(i) = HMAC_hash(secret, A(i-1))
// 这是 TLS 密钥派生的核心构造,用于生成主密钥和密钥块
func pHash(secret, seed []byte, outLen int, h func() hash.Hash) []byte {
// 预分配结果缓冲区
result := make([]byte, 0, outLen)
// A 为 HMAC 的迭代输入,初始为 seed
A := seed
// 循环生成直到输出长度满足要求
for len(result) < outLen {
// 计算 A(i) = HMAC_hash(secret, A(i-1))
A = hmacHash(secret, A, h)
// 计算 HMAC_hash(secret, A(i) + seed) 并追加到结果
result = append(result, hmacHash(secret, append(A, seed...), h)...)
}
// 截断到指定长度
return result[:outLen]
}

// hmacHash 使用指定的哈希函数计算 HMAC
// 这是 TLS PRF 构造的基础密码学原语
func hmacHash(secret, data []byte, h func() hash.Hash) []byte {
// 创建 HMAC 实例,使用 secret 作为密钥
mac := hmac.New(h, secret)
// 写入要签名的数据
mac.Write(data)
// 计算并返回 HMAC 值
return mac.Sum(nil)
}

// buildClientHello 构造 TLS ClientHello 消息
// ClientHello 是 TLS 握手的第一条消息,由客户端发起
// 包含:协议版本、随机数、会话ID、密码套件列表、压缩方法、扩展字段
// 返回值:完整的 TLS 记录层数据 + 客户端随机数
func buildClientHello(domain string) ([]byte, []byte) {
// 生成 32 字节的客户端随机值,用于密钥派生和握手完整性校验
random := make([]byte, 32)
if _, err := rand.Read(random); err != nil {
panic(err)
}

// 构造支持的密码套件列表(按优先级排序)
// 此处仅声明一个套件:ECDHE-ECDSA-AES128-GCM-SHA256 (0xC02B)
cipherSuites := []byte{
0xC0, 0x2B,
}
// 压缩方法列表:仅支持 null 压缩(不压缩)
compression := []byte{0x00}

// 扩展字段列表,动态构建
var exts []byte

// ====== SNI 扩展(Server Name Indication)======
// 用于支持虚拟主机,告知服务器请求的具体域名
sniDomain := []byte(domain)
sniSN := bytes.NewBuffer(nil)
sniSN.WriteByte(0x00) // 服务器名称类型:0x00 表示主机名
sniSN.Write([]byte{byte(len(sniDomain) >> 8), byte(len(sniDomain))}) // 主机名长度(大端序)
sniSN.Write(sniDomain) // 主机名数据
sniED := bytes.NewBuffer(nil)
sniED.Write([]byte{byte(sniSN.Len() >> 8), byte(sniSN.Len())}) // SNI 扩展数据长度
sniED.Write(sniSN.Bytes()) // SNI 扩展数据
sniE := bytes.NewBuffer(nil)
sniE.Write([]byte{0x00, 0x00}) // 扩展类型:SNI (0x0000)
sniE.Write([]byte{byte(sniED.Len() >> 8), byte(sniED.Len())}) // 扩展长度
sniE.Write(sniED.Bytes()) // 扩展数据
exts = append(exts, sniE.Bytes()...)

// ====== 签名算法扩展(Signature Algorithms)======
// 告知服务器客户端支持的签名算法,服务器在 ServerKeyExchange 中使用其一
// 格式:每个条目为 HashAlgorithm + SignatureAlgorithm 字节对
sigAlgs := []byte{
0x04, 0x03, // ECDSA 用 SHA-256
0x05, 0x03, // ECDSA 用 SHA-384
0x06, 0x03, // ECDSA 用 SHA-512
0x02, 0x03, // ECDSA 用 SHA-224
0x04, 0x01, // RSA 用 SHA-256
0x05, 0x01, // RSA 用 SHA-384
0x06, 0x01, // RSA 用 SHA-512
0x02, 0x01, // RSA 用 SHA-224
}
sigED := bytes.NewBuffer(nil)
sigED.Write([]byte{byte(len(sigAlgs) >> 8), byte(len(sigAlgs))}) // 算法列表长度
sigED.Write(sigAlgs) // 算法列表
sigE := bytes.NewBuffer(nil)
sigE.Write([]byte{0x00, 0x0D}) // 扩展类型:signature_algorithms (0x000D)
sigE.Write([]byte{byte(sigED.Len() >> 8), byte(sigED.Len())}) // 扩展长度
sigE.Write(sigED.Bytes()) // 扩展数据
exts = append(exts, sigE.Bytes()...)

// ====== 支持的组扩展(Supported Groups)======
// 告知服务器客户端支持的椭圆曲线组
// 格式:列表长度(2字节)+ 每个组的标识(2字节)
// 指定使用 secp256r1 (1.2.840.10045.3.1.7) 曲线
groups := []byte{0x00, 0x02, 0x00, 0x17} // 长度=2, secp256r1 (P-256)
gE := bytes.NewBuffer(nil)
gE.Write([]byte{0x00, 0x0A}) // 扩展类型:supported_groups (0x000A)
gE.Write([]byte{byte(len(groups) >> 8), byte(len(groups))}) // 扩展长度
gE.Write(groups) // 支持的组列表
exts = append(exts, gE.Bytes()...)

// ====== EC 点格式扩展(EC Point Formats)======
// 告知服务器客户端支持的椭圆曲线点编码格式
// 0x00 = 压缩格式, 0x01 = 未压缩格式
ecFmt := []byte{0x01, 0x00} // 长度=1, 支持未压缩格式
ecE := bytes.NewBuffer(nil)
ecE.Write([]byte{0x00, 0x0B}) // 扩展类型:ec_point_formats (0x000B)
ecE.Write([]byte{byte(len(ecFmt) >> 8), byte(len(ecFmt))}) // 扩展长度
ecE.Write(ecFmt) // 点格式列表
exts = append(exts, ecE.Bytes()...)

// ====== 安全重协商扩展(Renegotiation Info)======
// 用于防止 TLS 重协商攻击(RFC 5746)
riE := bytes.NewBuffer(nil)
riE.Write([]byte{0xFF, 0x01}) // 扩展类型:renegotiation_info (0xFF01)
riE.Write([]byte{0x00, 0x01, 0x00}) // 扩展长度=1, 数据=0x00(不支持安全重协商)
exts = append(exts, riE.Bytes()...)

// 组装扩展列表:总长度(2字节)+ 所有扩展
extList := bytes.NewBuffer(nil)
extList.Write([]byte{byte(len(exts) >> 8), byte(len(exts))})
extList.Write(exts)

// 构建 ClientHello 主体
body := bytes.NewBuffer(nil)
body.Write([]byte{0x03, 0x03}) // 协议版本:TLS 1.2 (0x0303)
body.Write(random) // 客户端随机数(32字节)
body.WriteByte(0x00) // 会话ID长度:0(新会话,不恢复)
body.Write([]byte{byte(len(cipherSuites) >> 8), byte(len(cipherSuites))}) // 密码套件列表长度
body.Write(cipherSuites) // 密码套件列表
body.WriteByte(0x01) // 压缩方法列表长度
body.Write(compression) // 压缩方法列表
body.Write(extList.Bytes()) // 扩展列表

// 构建握手消息:类型(0x01) + 长度(3字节) + 主体
handshakeBody := body.Bytes()
hs := bytes.NewBuffer(nil)
hs.WriteByte(0x01) // 握手消息类型:ClientHello = 1
hs.Write([]byte{byte(len(handshakeBody) >> 16), byte(len(handshakeBody) >> 8), byte(len(handshakeBody))}) // 消息长度(3字节大端序)
hs.Write(handshakeBody)
handshake := hs.Bytes()

// 构建 TLS 记录层:内容类型(0x16) + 版本(0x0303) + 长度(2字节) + 数据
record := bytes.NewBuffer(nil)
record.WriteByte(0x16) // 内容类型:Handshake = 22 (0x16)
record.Write([]byte{0x03, 0x03}) // 协议版本:TLS 1.2
record.Write([]byte{byte(len(handshake) >> 8), byte(len(handshake))}) // 记录长度
record.Write(handshake)
return record.Bytes(), random
}

// parseServerHandshake 解析服务器返回的所有握手消息
// 服务器在收到 ClientHello 后依次发送:ServerHello → Certificate → ServerKeyExchange → ServerHelloDone
// 该函数遍历所有 TLS 记录层,提取并解析每个握手消息
// 解析结果存储在 tlsState 结构中
func parseServerHandshake(data []byte, st *tlsState) error {
// 创建字节读取器
r := bytes.NewReader(data)
// 记录记录层索引
recIdx := 0
// 是否已收到 ServerHelloDone 消息(握手结束标志)
gotServerHelloDone := false
// 循环读取每个 TLS 记录层
for r.Len() > 0 {
recIdx++
// 读取记录层内容类型(1字节)
var typ [1]byte
if _, err := r.Read(typ[:]); err != nil {
return err
}
// 读取记录层协议版本(2字节)
var ver [2]byte
if _, err := r.Read(ver[:]); err != nil {
return err
}
// 读取记录层负载长度(2字节)
var length uint16
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
return err
}
// 读取负载数据
payload := make([]byte, length)
if _, err := io.ReadFull(r, payload); err != nil {
return err
}
// 打印记录层信息
fmt.Printf(" Record #%d: type=0x%02x version=%d.%d length=%d\n", recIdx, typ[0], ver[0], ver[1], length)

// 如果是握手消息(0x16 = Handshake),进一步解析内部的握手子消息
if typ[0] == 0x16 {
subR := bytes.NewReader(payload)
msgIdx := 0
// 在一个记录层中可能包含多个握手子消息
for subR.Len() > 0 {
msgIdx++
// 读取握手消息类型(1字节)
var msgType byte
if err := binary.Read(subR, binary.BigEndian, &msgType); err != nil {
return err
}
// 读取握手消息长度(3字节)
var lenBuf [3]byte
if _, err := io.ReadFull(subR, lenBuf[:]); err != nil {
return err
}
msgLen := uint32(lenBuf[0])<<16 | uint32(lenBuf[1])<<8 | uint32(lenBuf[2])
// 读取握手消息数据
msgData := make([]byte, msgLen)
if _, err := io.ReadFull(subR, msgData); err != nil {
return err
}
// 握手消息类型名称映射
typeNames := map[byte]string{
0x02: "ServerHello", // 服务器问候
0x0B: "Certificate", // 服务器证书
0x0C: "ServerKeyExchange", // 服务器密钥交换
0x0D: "CertificateRequest", // 证书请求(可选)
0x0E: "ServerHelloDone", // 服务器问候结束
}
typeName := typeNames[msgType]
if typeName == "" {
typeName = fmt.Sprintf("Unknown(0x%02x)", msgType)
}
fmt.Printf(" Msg #%d: type=%s length=%d\n", msgIdx, typeName, msgLen)

// 根据消息类型分别处理
switch msgType {
case 0x02: // ServerHello
if len(msgData) < 34 {
return fmt.Errorf("server hello too short")
}
// 提取服务器随机数(第3-34字节,共32字节)
st.serverRand = make([]byte, 32)
copy(st.serverRand, msgData[2:34])
// 解析会话ID
sessionIDLen := int(msgData[34])
offset := 35 + sessionIDLen
if offset+3 > len(msgData) {
return fmt.Errorf("server hello too short for cipher")
}
// 解析协商的密码套件(2字节大端序)
st.cipherSuite = binary.BigEndian.Uint16(msgData[offset : offset+2])
// 解析压缩方法
compressionMethod := msgData[offset+2]
fmt.Printf(" ServerHello: sessionIDLen=%d, cipherSuite=0x%04x, compressionMethod=%d\n",
sessionIDLen, st.cipherSuite, compressionMethod)
// 解析 ServerHello 扩展(可选)
if offset+2 < len(msgData) {
extTotalLen := int(binary.BigEndian.Uint16(msgData[offset+2 : offset+4]))
extEnd := offset + 4 + extTotalLen
fmt.Printf(" ServerHello extensions total length=%d\n", extTotalLen)
off := offset + 4
for off < extEnd && off+4 <= len(msgData) {
extType := binary.BigEndian.Uint16(msgData[off : off+2])
extLen := int(binary.BigEndian.Uint16(msgData[off+2 : off+4]))
fmt.Printf(" Extension: type=0x%04x length=%d\n", extType, extLen)
off += 4 + extLen
}
}
case 0x0B: // Certificate
if len(msgData) < 3 {
return fmt.Errorf("certificate too short")
}
// 解析证书列表长度
certListLen := uint32(msgData[0])<<16 | uint32(msgData[1])<<8 | uint32(msgData[2])
certData := msgData[3 : 3+certListLen]
// 解析第一个证书的长度
certLen := uint32(certData[0])<<16 | uint32(certData[1])<<8 | uint32(certData[2])
fmt.Printf(" Certificate: certListLen=%d, first cert len=%d\n", certListLen, certLen)
// 保存第一个证书的 DER 编码(用于签名验证)
st.certDER = certData[3 : 3+certLen]
case 0x0C: // ServerKeyExchange
// 解析 ServerKeyExchange,提取曲线、公钥、签名算法等信息
if err := parseServerKeyExchange(msgData, st); err != nil {
return fmt.Errorf("ServerKeyExchange parse: %v", err)
}
// 存储 ServerKeyExchange 的完整消息(用于 Finished 校验)
skxPayload := bytes.NewBuffer(nil)
skxPayload.WriteByte(0x0C)
skxPayload.Write([]byte{byte(len(msgData) >> 16), byte(len(msgData) >> 8), byte(len(msgData))})
skxPayload.Write(msgData)
fmt.Printf(" ServerKeyExchange: curve=0x%04x, serverPubKey len=%d\n",
st.skxCurve, len(st.skxServerPub))
case 0x0E: // ServerHelloDone
// 标记 ServerHelloDone 已接收,客户端可以开始发送密钥交换消息
gotServerHelloDone = true
fmt.Println(" ServerHelloDone")
}
}
}
// 收到 ServerHelloDone 后握手消息结束
if gotServerHelloDone {
break
}
// 如果是 Alert 消息(0x15),表示服务器返回错误
if typ[0] == 0x15 {
if len(payload) >= 2 {
return fmt.Errorf("alert received: level=%d, desc=%s", payload[0], alertMsg(payload[1]))
}
return fmt.Errorf("alert received: %x", payload)
}
}
return nil
}

// parseServerKeyExchange 解析 ServerKeyExchange 消息
// ServerKeyExchange 包含:曲线类型、曲线参数、服务器公钥、签名算法和签名
// 该消息用于 ECDHE 密钥交换,服务器将其公钥发送给客户端
func parseServerKeyExchange(data []byte, st *tlsState) error {
if len(data) < 4 {
return fmt.Errorf("too short")
}
// 读取曲线类型:0x03 表示命名曲线(named_curve)
curveType := data[0]
if curveType != 0x03 {
return fmt.Errorf("unsupported curve type: %d", curveType)
}
// 读取曲线编号(2字节大端序),如 0x001D = X25519
st.skxCurve = binary.BigEndian.Uint16(data[1:3])
offset := 3

// 读取服务器公钥长度(1字节)
pubKeyLen := int(data[offset])
offset++
if offset+pubKeyLen > len(data) {
return fmt.Errorf("server pub key exceeds data")
}
// 保存服务器公钥字节
st.skxServerPub = data[offset : offset+pubKeyLen]
offset += pubKeyLen

// 读取签名算法:哈希算法(1字节) + 签名算法(1字节)
if offset+2 > len(data) {
return fmt.Errorf("missing signature algorithm")
}
st.skxHashAlg = data[offset] // 如 0x04 = SHA-256, 0x05 = SHA-384
st.skxSigAlg = data[offset+1] // 如 0x03 = ECDSA, 0x01 = RSA
offset += 2
fmt.Printf(" ServerKeyExchange: hash=0x%02x sig=0x%02x\n",
st.skxHashAlg, st.skxSigAlg)

// 读取签名长度(2字节)
sigLen := int(binary.BigEndian.Uint16(data[offset : offset+2]))
offset += 2
if offset+sigLen > len(data) {
return fmt.Errorf("signature exceeds data")
}
// 提取服务器签名
sig := data[offset : offset+sigLen]

// 验证服务器签名,确保 ServerKeyExchange 消息未被篡改
if err := verifyServerKeyExchangeSignature(data, st, sig); err != nil {
return fmt.Errorf("signature verify failed: %v", err)
}
fmt.Println(" ServerKeyExchange signature verified OK")
return nil
}

// ecdsaSigValue 表示 ECDSA 签名的 (R, S) 两个大整数值
// ECDSA 签名由两个随机大整数 R 和 S 组成,ASN.1 DER 编码
type ecdsaSigValue struct {
R, S *big.Int
}

// verifyServerKeyExchangeSignature 验证服务器对 ServerKeyExchange 消息的签名
// TLS 中服务器使用其证书私钥对 ServerKeyExchange 进行签名
// 客户端使用服务器证书中的公钥验证签名,确保:
// 1) 服务器确实拥有证书对应的私钥(身份认证)
// 2) ServerKeyExchange 消息未被篡改(完整性保护)
// 3) 密钥交换参数(曲线、公钥)是真实的
func verifyServerKeyExchangeSignature(skxData []byte, st *tlsState, sigDER []byte) error {
// 解析 ASN.1 DER 编码的 ECDSA 签名,提取 R 和 S 值
var sig ecdsaSigValue
if rest, err := asn1.Unmarshal(sigDER, &sig); err != nil {
return fmt.Errorf("DER parse: %v", err)
} else if len(rest) > 0 {
return fmt.Errorf("trailing data after signature")
}

// 构建签名验证数据,格式为:client_random || server_random || curve_type || curve || pubkey_length || pubkey
// 这是 TLS 规定的签名输入(RFC 5246 Section 7.4.1.4.1)
verifyData := bytes.NewBuffer(nil)
verifyData.Write(st.clientRand) // 客户端随机数
verifyData.Write(st.serverRand) // 服务器随机数
verifyData.Write([]byte{0x03}) // 曲线类型:命名曲线
verifyData.Write([]byte{byte(st.skxCurve >> 8), byte(st.skxCurve)}) // 曲线编号
pubKeyLen := len(st.skxServerPub)
verifyData.Write([]byte{byte(pubKeyLen)}) // 公钥长度
verifyData.Write(st.skxServerPub) // 服务器公钥

// 根据协商的哈希算法计算验证数据的哈希值
var hashBytes []byte
switch st.skxHashAlg {
case 0x04: // SHA-256
h := sha256.Sum256(verifyData.Bytes())
hashBytes = h[:]
case 0x05: // SHA-384
h := sha512.Sum384(verifyData.Bytes())
hashBytes = h[:]
case 0x06: // SHA-512
h := sha512.Sum512(verifyData.Bytes())
hashBytes = h[:]
default:
return fmt.Errorf("unsupported hash algorithm: 0x%02x", st.skxHashAlg)
}

// 解析服务器证书,提取公钥
cert, err := x509.ParseCertificate(st.certDER)
if err != nil {
return err
}
st.serverPubKey = cert.PublicKey

// 根据证书公钥类型选择相应的签名验证方法
switch pub := cert.PublicKey.(type) {
case *ecdsa.PublicKey:
// ECDSA 签名验证
if !ecdsa.Verify(pub, hashBytes, sig.R, sig.S) {
return fmt.Errorf("ECDSA signature verification failed")
}
fmt.Printf(" ECDSA signature verified (hash=0x%02x)\n", st.skxHashAlg)
case *rsa.PublicKey:
// RSA PKCS#1 v1.5 签名验证
cryptoHash := crypto.SHA256
switch st.skxHashAlg {
case 0x04:
cryptoHash = crypto.SHA256
case 0x05:
cryptoHash = crypto.SHA384
case 0x06:
cryptoHash = crypto.SHA512
}
if err := rsa.VerifyPKCS1v15(pub, cryptoHash, hashBytes, sigDER); err != nil {
return fmt.Errorf("RSA signature verification failed: %v", err)
}
fmt.Printf(" RSA signature verified (hash=0x%02x)\n", st.skxHashAlg)
default:
return fmt.Errorf("unsupported public key type")
}
return nil
}

// generateECDHEKeyPair 根据指定曲线生成 ECDHE 密钥对
// 客户端生成临时密钥对用于 ECDHE 密钥交换
// 公钥发送给服务器,私钥用于计算共享密钥
// 这保证了前向保密(Forward Secrecy):即使服务器私钥泄露,过去的会话仍安全
func generateECDHEKeyPair(curve uint16) (*ecdh.PrivateKey, *ecdh.PublicKey, error) {
switch curve {
case CURVE_X25519:
// 使用 X25519 曲线生成密钥对
// X25519 提供约 128 位安全强度,性能优秀
priv, err := ecdh.X25519().GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
return priv, priv.PublicKey(), nil
case CURVE_SECP256R1:
// 使用 secp256r1 (P-256) 曲线生成密钥对
// P-256 提供约 128 位安全强度,广泛支持
priv, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
return priv, priv.PublicKey(), nil
default:
return nil, nil, fmt.Errorf("unsupported curve: 0x%04x", curve)
}
}

// buildClientKeyExchangeECDH 构造 ClientKeyExchange 消息
// 客户端将其 ECDHE 公钥发送给服务器
// 格式:客户端公钥长度(1字节) || 客户端公钥
// 然后封装为 TLS 握手消息和 TLS 记录层
func buildClientKeyExchangeECDH(pubKey *ecdh.PublicKey) []byte {
// 将公钥序列化为字节
pubBytes := pubKey.Bytes()
// 构建握手消息主体:公钥长度 + 公钥
body := bytes.NewBuffer(nil)
body.Write([]byte{byte(len(pubBytes))})
body.Write(pubBytes)
handshakeBody := body.Bytes()

// 构建握手消息:类型(0x10=ClientKeyExchange) + 长度(3字节) + 主体
hs := bytes.NewBuffer(nil)
hs.WriteByte(0x10)
hs.Write([]byte{byte(len(handshakeBody) >> 16), byte(len(handshakeBody) >> 8), byte(len(handshakeBody))})
hs.Write(handshakeBody)
handshake := hs.Bytes()

// 构建 TLS 记录层:Handshake(0x16) + TLS 1.2(0x0303) + 长度 + 数据
record := bytes.NewBuffer(nil)
record.WriteByte(0x16)
record.Write([]byte{0x03, 0x03})
record.Write([]byte{byte(len(handshake) >> 8), byte(len(handshake))})
record.Write(handshake)
return record.Bytes()
}

// computeSharedSecretECDH 根据指定曲线计算 ECDHE 共享密钥
// 客户端使用自己的私钥和服务器的公钥计算共享密钥
// 返回的共享密钥即为 TLS 中的预主密钥(pre_master_secret)
// 支持 X25519 和 secp256r1 (P-256) 曲线
func computeSharedSecretECDH(curve uint16, priv *ecdh.PrivateKey, serverPubBytes []byte) ([]byte, error) {
var serverPub *ecdh.PublicKey
var err error

switch curve {
case CURVE_X25519:
// 将服务器公钥字节解析为 X25519 公钥对象
serverPub, err = ecdh.X25519().NewPublicKey(serverPubBytes)
if err != nil {
return nil, fmt.Errorf("parse server X25519 pub: %v", err)
}
case CURVE_SECP256R1:
// 将服务器公钥字节解析为 P-256 公钥对象
// P-256 公钥格式:0x04 || X(32字节) || Y(32字节) = 65 字节(未压缩)
serverPub, err = ecdh.P256().NewPublicKey(serverPubBytes)
if err != nil {
return nil, fmt.Errorf("parse server P-256 pub: %v", err)
}
default:
return nil, fmt.Errorf("unsupported curve for shared secret: 0x%04x", curve)
}

// 执行 ECDH 运算:shared_secret = client_private * server_public
secret, err := priv.ECDH(serverPub)
if err != nil {
return nil, fmt.Errorf("ECDH compute: %v", err)
}
return secret, nil
}

// deriveKeysAEAD 使用 TLS PRF 派生 AEAD 密码套件所需的密钥
// 密钥派生过程(RFC 5246 Section 6.3):
//
// master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) [0..47]
// key_block = PRF(master_secret, "key expansion", ServerHello.random + ClientHello.random) [0..key_len*2+7]
// client_write_key = key_block[0..key_len-1]
// server_write_key = key_block[key_len..2*key_len-1]
// client_write_IV = key_block[2*key_len..2*key_len+3]
// server_write_IV = key_block[2*key_len+4..2*key_len+7]
//
// 对于 GCM 模式,IV 为 4 字节(隐式部分),实际 nonce = IV(4字节) || explicit_nonce(8字节,通常为序列号)
func deriveKeysAEAD(preMaster, clientRand, serverRand []byte, keyLen int, useSHA384 bool) ([]byte, []byte, []byte, []byte) {
// 从预主密钥派生主密钥(48字节)
masterSecret := prf12(preMaster, []byte("master secret"), clientRand, serverRand, 48, useSHA384)
fmt.Printf(" masterSecret=%x\n", masterSecret)

// 从主密钥派生密钥块
// key_block 长度 = key_len*2(两个写密钥)+ 4*2(两个 IV,各4字节)
keyBlock := prf12(masterSecret, []byte("key expansion"), serverRand, clientRand, keyLen*2+8, useSHA384)
clientWriteKey := keyBlock[0:keyLen]
serverWriteKey := keyBlock[keyLen : keyLen*2]
clientWriteIV := keyBlock[keyLen*2 : keyLen*2+4]
serverWriteIV := keyBlock[keyLen*2+4 : keyLen*2+8]
fmt.Printf(" clientWriteKey=%x\n", clientWriteKey)
fmt.Printf(" serverWriteKey=%x\n", serverWriteKey)
fmt.Printf(" clientWriteIV=%x\n", clientWriteIV)
fmt.Printf(" serverWriteIV=%x\n", serverWriteIV)

return clientWriteKey, serverWriteKey, clientWriteIV, serverWriteIV
}

// encryptGCM 使用 AES-GCM 对数据进行加密
// TLS 1.2 GCM 模式加密(RFC 5288):
//
// nonce = implicit_IV(4字节,来自密钥派生) || explicit_nonce(8字节,序列号)
// AAD = 序列号(8字节) || 内容类型(1字节) || 版本(2字节) || 明文长度(2字节)
// 输出 = explicit_nonce || 密文 || 认证标签(16字节)
//
// GCM 提供认证加密(AEAD),同时保证机密性和完整性
func encryptGCM(data, writeKey, iv []byte, seqNum uint64, contentType byte) []byte {
// 保存明文长度
plainLen := len(data)

// 构造 12 字节 GCM nonce
// 前 4 字节为隐式 IV(来自密钥派生),后 8 字节为显式 nonce(使用序列号)
nonce := make([]byte, 12)
copy(nonce[0:4], iv)
binary.BigEndian.PutUint64(nonce[4:12], seqNum)

// 构造附加认证数据(AAD),用于 GCM 的完整性校验
// AAD 格式:sequence_number(8) || content_type(1) || protocol_version(2) || record_length(2)
aad := make([]byte, 0, 15)
seqBytes := make([]byte, 8)
binary.BigEndian.PutUint64(seqBytes, seqNum)
aad = append(aad, seqBytes...)
aad = append(aad, contentType)
aad = append(aad, 0x03, 0x03) // TLS 1.2
aad = append(aad, byte(plainLen>>8), byte(plainLen))

// 创建 AES 密码块
block, err := aes.NewCipher(writeKey)
if err != nil {
panic(err)
}
// 创建 GCM 加密器
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}

// 执行 GCM 加密:输出 = 密文 || 16字节认证标签
ciphertext := aesgcm.Seal(nil, nonce, data, aad)

// 构造最终 TLS GCM 记录:explicit_nonce(8字节) || 密文+标签
nonceExplicit := make([]byte, 8)
binary.BigEndian.PutUint64(nonceExplicit, seqNum)
result := append(nonceExplicit, ciphertext...)
return result
}

// decryptGCM 使用 AES-GCM 对数据进行解密
// 输入格式:explicit_nonce(8字节) || 密文 || 认证标签(16字节)
// 从记录中提取 explicit nonce,与隐式 IV 合并成完整 nonce
// 并使用相同的 AAD 进行完整性验证
// 如果认证失败,返回错误表示数据已被篡改
func decryptGCM(ciphertext, writeKey, iv []byte, seqNum uint64, contentType byte) ([]byte, error) {
// 检查最小长度:8字节 nonce + 16字节 GCM 标签
if len(ciphertext) < 8+16 {
return nil, fmt.Errorf("ciphertext too short for nonce+tag")
}

// 从记录中提取 explicit nonce(前 8 字节)
explicitNonce := ciphertext[:8]
// 实际密文部分
actualCiphertext := ciphertext[8:]
// 计算明文长度 = 密文长度 - 16字节认证标签
plainLen := len(actualCiphertext) - 16

// 构造完整的 12 字节 nonce = implicit_IV(4) || explicit_nonce(8)
nonce := make([]byte, 12)
copy(nonce[0:4], iv)
copy(nonce[4:12], explicitNonce)

// 构造与加密端相同的 AAD
aad := make([]byte, 0, 15)
seqBytes := make([]byte, 8)
binary.BigEndian.PutUint64(seqBytes, seqNum)
aad = append(aad, seqBytes...)
aad = append(aad, contentType)
aad = append(aad, 0x03, 0x03) // TLS 1.2
aad = append(aad, byte(plainLen>>8), byte(plainLen))

// 创建 AES 密码块
block, err := aes.NewCipher(writeKey)
if err != nil {
return nil, err
}
// 创建 GCM 解密器
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

// 执行 GCM 解密和完整性验证
// 如果 AAD 或密文被篡改,Open 会返回错误
plaintext, err := aesgcm.Open(nil, nonce, actualCiphertext, aad)
if err != nil {
return nil, fmt.Errorf("GCM decrypt/verify failed: %v", err)
}
return plaintext, nil
}

// sendRecord 发送一个 TLS 记录层
// 将负载数据使用 GCM 加密后,封装为 TLS 记录层格式发送
// TLS 记录层格式:内容类型(1) || 版本(2) || 长度(2) || 数据
// 返回值:sentBytes(发送到网络的完整TLS记录层字节), err(错误)
func sendRecord(conn net.Conn, contentType byte, payload, writeKey, iv []byte, seqNum uint64) ([]byte, error) {
// 使用 GCM 加密负载
encrypted := encryptGCM(payload, writeKey, iv, seqNum, contentType)
// 构建 TLS 记录层
record := bytes.NewBuffer(nil)
record.WriteByte(contentType) // 内容类型
record.Write([]byte{0x03, 0x03}) // TLS 1.2
record.Write([]byte{byte(len(encrypted) >> 8), byte(len(encrypted))}) // 长度
record.Write(encrypted) // 加密后的数据

// 发送到网络
_, err := conn.Write(record.Bytes())
return record.Bytes(), err
}

// recvRecord 接收一个 TLS 记录层
// 从网络读取记录层头,解密负载数据
// 特殊处理:ChangeCipherSpec (0x14) 始终以明文形式传输
// 其他记录(Alert、Handshake、Application Data)都需要解密
// 返回值:contentType(内容类型), plaintext(解密后明文), rawRecord(完整原始记录字节), err(错误)
func recvRecord(conn net.Conn, readKey, iv []byte, seqNum uint64) (contentType byte, plaintext []byte, rawRecord []byte, err error) {
// 读取 5 字节记录层头:内容类型(1) + 版本(2) + 长度(2)
header := make([]byte, 5)
_, err = io.ReadFull(conn, header)
if err != nil {
return 0, nil, nil, err
}
contentType = header[0]
length := binary.BigEndian.Uint16(header[3:5])

// ChangeCipherSpec (0x14) 在 TLS 1.2 中始终以明文传输,不加密
if contentType == 0x14 {
payload := make([]byte, length)
io.ReadFull(conn, payload)
rawRecord = append(header, payload...)

return contentType, payload, rawRecord, nil
}

// 其他所有记录类型(Alert 0x15, Handshake 0x16, App Data 0x17)都经过加密
encrypted := make([]byte, length)
if _, err := io.ReadFull(conn, encrypted); err != nil {
return 0, nil, nil, err
}
rawRecord = append(header, encrypted...)

// Application Data (0x17) 的打印由调用方处理,此处仅处理 Alert 和 Handshake

// 使用 GCM 解密
plaintext, err = decryptGCM(encrypted, readKey, iv, seqNum, contentType)
if err != nil {
return 0, nil, nil, err
}

// 解密后如果是 Alert 消息,打印详细信息
if contentType == 0x15 {
fmt.Printf("<<< Alert (decrypted): level=%d desc=%s\n", plaintext[0], alertMsg(plaintext[1]))
return contentType, plaintext, rawRecord, nil
}

return contentType, plaintext, rawRecord, nil
}

// containsServerHelloDone 检查服务器握手数据中是否包含 ServerHelloDone 消息
// ServerHelloDone 表示服务器已发送完所有握手消息,等待客户端响应
// 该函数用于 readFullHandshake 中判断握手消息是否完整
func containsServerHelloDone(payload []byte) bool {
r := bytes.NewReader(payload)
for r.Len() >= 4 {
var msgType byte
if err := binary.Read(r, binary.BigEndian, &msgType); err != nil {
return false
}
var lenBuf [3]byte
if _, err := io.ReadFull(r, lenBuf[:]); err != nil {
return false
}
msgLen := uint32(lenBuf[0])<<16 | uint32(lenBuf[1])<<8 | uint32(lenBuf[2])
// ServerHelloDone: type=0x0E, length=0(空消息)
if msgType == 0x0E && msgLen == 0 {
return true
}
// 跳过当前消息继续查找
skip := make([]byte, msgLen)
if msgLen > 0 {
if _, err := io.ReadFull(r, skip); err != nil {
return false
}
}
}
return false
}

// readFullHandshake 从连接读取完整的服务器握手消息
// 持续读取 TLS 记录层,直到检测到 ServerHelloDone 或 ChangeCipherSpec
// 返回所有原始记录层数据、记录数量和可能的错误
func readFullHandshake(conn net.Conn) ([]byte, int, error) {
var all []byte
recIdx := 0
for {
// 读取 5 字节记录层头
header := make([]byte, 5)
n, err := io.ReadFull(conn, header)
if err != nil {
return all, recIdx, nil
}
recIdx++
typ := header[0]
length := binary.BigEndian.Uint16(header[3:5])
fmt.Printf(" readFullHandshake #%d: type=0x%02x length=%d (header bytes: %d)\n", recIdx, typ, length, n)
// 读取负载
payload := make([]byte, length)
if _, err := io.ReadFull(conn, payload); err != nil {
return nil, recIdx, err
}
// 追加到完整数据
all = append(all, header...)
all = append(all, payload...)
// 如果是握手记录且包含 ServerHelloDone,则停止读取
if typ == 0x16 {
if containsServerHelloDone(payload) {
fmt.Printf(" Found ServerHelloDone, total read=%d\n", len(all))
break
}
}
// 如果遇到 ChangeCipherSpec,也停止读取
if typ == 0x14 {
fmt.Printf(" Found ChangeCipherSpec, total read=%d\n", len(all))
break
}
}
return all, recIdx, nil
}

// extractHandshakeMessages 从 TLS 记录层数据中提取所有握手消息的原始字节
// 用于计算 Finished 消息的哈希值
// Finished 消息需要对所有之前发送和接收的握手消息内容进行哈希
func extractHandshakeMessages(tlsRecords []byte) []byte {
var handshakeMsgs []byte
r := bytes.NewReader(tlsRecords)
for r.Len() >= 5 {
var typ [1]byte
if _, err := r.Read(typ[:]); err != nil {
break
}
var ver [2]byte
if _, err := r.Read(ver[:]); err != nil {
break
}
var length uint16
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
break
}
payload := make([]byte, length)
if _, err := io.ReadFull(r, payload); err != nil {
break
}
// 仅提取 Handshake 类型的记录(0x16),忽略 Alert 等其他类型
if typ[0] == 0x16 {
handshakeMsgs = append(handshakeMsgs, payload...)
}
}
return handshakeMsgs
}

// main 函数实现完整的 TLS 1.2 握手流程
// TLS 1.2 握手流程(ECDHE 密钥交换):
// 1. 客户端 → 服务器: ClientHello
// 2. 服务器 → 客户端: ServerHello + Certificate + ServerKeyExchange + ServerHelloDone
// 3. 客户端 → 服务器: ClientKeyExchange + ChangeCipherSpec + Finished
// 4. 服务器 → 客户端: ChangeCipherSpec + Finished
// 5. 双方使用派生密钥进行加密通信
func main() {
// 服务器地址和域名
target := "yishuifengxiao.com:443"
domain := "yishuifengxiao.com"

// 建立 TCP 连接
conn, err := net.Dial("tcp", target)
if err != nil {
panic(err)
}
defer conn.Close()

// 创建 TLS 状态结构
st := &tlsState{domain: domain}

// ====== 步骤 1: 发送 ClientHello ======
// ClientHello 包含:协议版本、随机数、密码套件、扩展字段等
clientHello, clientRand := buildClientHello(domain)
st.clientRand = clientRand
// 保存 ClientHello 的握手消息体(不含 TLS 记录层头),用于 Finished 哈希计算
clientHelloHsMsg := clientHello[5:]
fmt.Printf(">>> ClientHello (%d bytes)\n", len(clientHello))

if _, err := conn.Write(clientHello); err != nil {
panic(err)
}
st.clientSeq++

// ====== 步骤 2: 读取服务器握手消息 ======
// 服务器依次发送:ServerHello → Certificate → ServerKeyExchange → ServerHelloDone
serverData, serverRecCount, err := readFullHandshake(conn)
if err != nil {
panic(err)
}
fmt.Printf("<<< Received %d bytes of server handshake (%d records)\n", len(serverData), serverRecCount)

st.serverSeq = uint64(serverRecCount)

// ====== 步骤 3: 解析服务器握手消息 ======
// 提取服务器随机数、证书、密码套件、密钥交换参数等
if err := parseServerHandshake(serverData, st); err != nil {
panic(err)
}

// ====== 步骤 4: 检查协商的密码套件 ======
switch st.cipherSuite {
case TLS_ECDHE_ECDSA_AES256_GCM_SHA384:
fmt.Println(" Negotiated: ECDHE-ECDSA-AES256-GCM-SHA384")
case TLS_ECDHE_ECDSA_AES128_GCM_SHA256:
fmt.Println(" Negotiated: ECDHE-ECDSA-AES128-GCM-SHA256")
case TLS_ECDHE_RSA_AES256_GCM_SHA384:
fmt.Println(" Negotiated: ECDHE-RSA-AES256-GCM-SHA384")
case TLS_ECDHE_RSA_AES128_GCM_SHA256:
fmt.Println(" Negotiated: ECDHE-RSA-AES128-GCM-SHA256")
default:
panic(fmt.Sprintf("unsupported cipher suite: 0x%04x", st.cipherSuite))
}

// ====== 步骤 5: ECDHE 密钥交换 ======
// 客户端生成自己的 ECDHE 密钥对
priv, pub, err := generateECDHEKeyPair(st.skxCurve)
if err != nil {
panic(err)
}
st.clientECPriv = priv
st.clientECPub = pub

// 根据曲线类型打印日志
curveName := "unknown"
switch st.skxCurve {
case CURVE_X25519:
curveName = "X25519"
case CURVE_SECP256R1:
curveName = "secp256r1 (P-256)"
}
fmt.Printf(" Generated %s key pair\n", curveName)

// 打印客户端 ECDHE 密钥对详情
privBytes := priv.Bytes()
pubBytes := pub.Bytes()
fmt.Printf(" clientECPriv=%x (len=%d)\n", privBytes, len(privBytes))
fmt.Printf(" clientECPub=%x (len=%d)\n", pubBytes, len(pubBytes))

// 使用客户端私钥和服务器公钥计算共享密钥(预主密钥)
sharedSecret, err := computeSharedSecretECDH(st.skxCurve, priv, st.skxServerPub)
if err != nil {
panic(err)
}
st.sharedSecret = sharedSecret
fmt.Printf(" sharedSecret=%x\n", sharedSecret)

// ====== 步骤 6: 发送 ClientKeyExchange ======
// 将客户端的 ECDHE 公钥发送给服务器
cke := buildClientKeyExchangeECDH(pub)
ckeHsMsg := cke[5:] // 保存握手消息体,用于 Finished 哈希
fmt.Printf(">>> ClientKeyExchange (%d bytes)\n", len(cke))

if _, err := conn.Write(cke); err != nil {
panic(err)
}
st.clientSeq++

// ====== 步骤 7: 密钥派生 ======
// 根据密码套件确定密钥长度和哈希算法
var keyLen int
useSHA384 := false
switch st.cipherSuite {
case TLS_ECDHE_ECDSA_AES128_GCM_SHA256, TLS_ECDHE_RSA_AES128_GCM_SHA256:
keyLen = 16 // AES-128 密钥长度 = 16 字节
default:
keyLen = 32 // AES-256 密钥长度 = 32 字节
useSHA384 = true
}

// 手动派生主密钥和密钥(与 deriveKeysAEAD 逻辑相同)
st.masterSecret = prf12(sharedSecret, []byte("master secret"), st.clientRand, st.serverRand, 48, useSHA384)
// key_block = client_write_key || server_write_key || client_write_IV || server_write_IV
keyBlock := prf12(st.masterSecret, []byte("key expansion"), st.serverRand, st.clientRand, keyLen*2+8, useSHA384)
st.clientWriteKey = keyBlock[0:keyLen]
st.serverWriteKey = keyBlock[keyLen : keyLen*2]
st.clientWriteIV = keyBlock[keyLen*2 : keyLen*2+4]
st.serverWriteIV = keyBlock[keyLen*2+4 : keyLen*2+8]
fmt.Printf(" masterSecret=%x\n", st.masterSecret)
fmt.Printf(" clientWriteKey=%x\n", st.clientWriteKey)
fmt.Printf(" serverWriteKey=%x\n", st.serverWriteKey)
fmt.Printf(" clientWriteIV=%x\n", st.clientWriteIV)
fmt.Printf(" serverWriteIV=%x\n", st.serverWriteIV)

// ====== 步骤 8: 发送 ChangeCipherSpec ======
// ChangeCipherSpec (0x14) 通知服务器后续消息将使用新密钥加密
// 该消息始终以明文传输
ccs := []byte{0x14, 0x03, 0x03, 0x00, 0x01, 0x01}
if _, err := conn.Write(ccs); err != nil {
panic(err)
}
fmt.Println(">>> ChangeCipherSpec sent")

st.clientSeq++
// RFC 5246 Section 6.1: 激活新密码状态时序列号重置为 0
st.clientSeq = 0

// ====== 步骤 9: 计算并发送 Finished 消息 ======
// Finished 消息是握手的最后一条消息,用于验证密钥交换的完整性
// 它包含对所有握手消息的哈希校验值

// 提取服务器握手消息的原始字节
serverHsMsgs := extractHandshakeMessages(serverData)
// 拼接所有握手消息:ClientHello + 服务器握手消息 + ClientKeyExchange
allHsMsgs := append(clientHelloHsMsg, serverHsMsgs...)
allHsMsgs = append(allHsMsgs, ckeHsMsg...)
fmt.Printf(" Handshake messages: clientHello=%d + server=%d + cke=%d = total %d bytes\n",
len(clientHelloHsMsg), len(serverHsMsgs), len(ckeHsMsg), len(allHsMsgs))

fmt.Printf(" clientHelloHsMsg=%x\n", clientHelloHsMsg)
fmt.Printf(" serverHsMsgs=%x\n", serverHsMsgs)
fmt.Printf(" ckeHsMsg=%x\n", ckeHsMsg)

// 计算所有握手消息的哈希值
var hsHash []byte
if useSHA384 {
h := sha512.Sum384(allHsMsgs)
hsHash = h[:]
fmt.Printf(" Handshake messages SHA-384 hash=%x\n", hsHash)
} else {
h := sha256.Sum256(allHsMsgs)
hsHash = h[:]
fmt.Printf(" Handshake messages SHA-256 hash=%x\n", hsHash)
}

// 计算 Finished 验证数据
// verify_data = PRF(master_secret, "client finished", Hash(handshake_messages))
verifyData := prf12(st.masterSecret, []byte("client finished"), hsHash, nil, 12, useSHA384)
fmt.Printf(" verify_data=%x\n", verifyData)

// 构建 Finished 握手消息:类型(0x14) + 长度(0x00000C) + verify_data
finishedMsg := bytes.NewBuffer(nil)
finishedMsg.WriteByte(0x14)
finishedMsg.Write([]byte{0x00, 0x00, 0x0C})
finishedMsg.Write(verifyData)
fmt.Printf(" finishedMsg (%d bytes): %x\n", finishedMsg.Len(), finishedMsg.Bytes())

// 使用新密钥加密 Finished 消息后发送
plainFinished := finishedMsg.Bytes()
encryptedFinished := encryptGCM(plainFinished, st.clientWriteKey, st.clientWriteIV, st.clientSeq, 0x16)

// 构建 Finished 的 TLS 记录层(加密后的)
finishedHeader := bytes.NewBuffer(nil)
finishedHeader.WriteByte(0x16) // Handshake 类型
finishedHeader.Write([]byte{0x03, 0x03}) // TLS 1.2
finishedHeader.Write([]byte{byte(len(encryptedFinished) >> 8), byte(len(encryptedFinished))})
finishedHeader.Write(encryptedFinished)
finishedRecord := finishedHeader.Bytes()
fmt.Printf(" finishedRecord (%d bytes)\n", len(finishedRecord))

if _, err := conn.Write(finishedRecord); err != nil {
panic(err)
}
fmt.Println(">>> Finished sent (encrypted)")
st.clientSeq++

// ====== 步骤 10: 接收服务器 ChangeCipherSpec ======
// 服务器确认密钥切换
contentType, _, _, err := recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq)
if err != nil {
panic(fmt.Sprintf("recv CCS error: %v", err))
}
if contentType != 0x14 {
panic(fmt.Sprintf("expected ChangeCipherSpec, got 0x%02x", contentType))
}

st.serverSeq++
// 服务端序列号在新 epoch 也重置为 0
st.serverSeq = 0

// ====== 步骤 11: 接收服务器 Finished ======
// 服务器的 Finished 消息验证整个握手过程的完整性
contentType, _, _, err = recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq)
if err != nil {
panic(fmt.Sprintf("recv Finished error: %v", err))
}
if contentType != 0x16 {
panic(fmt.Sprintf("expected Finished, got 0x%02x", contentType))
}

st.serverSeq++

// ====== 步骤 12: 发送 HTTP 请求(应用数据)======
// TLS 握手完成,现在使用加密通道发送 HTTP 请求
httpReq := "GET / HTTP/1.1\r\nHost: " + domain + "\r\nConnection: close\r\nUser-Agent: manual-tls\r\n\r\n"
httpData := []byte(httpReq)

// --- Application Data 原文 (明文) ---
fmt.Println("========== 发送的 Application Data ==========")

// 加密一次,避免 sendRecord 内部再次加密造成重复计算
sendEncrypted := encryptGCM(httpData, st.clientWriteKey, st.clientWriteIV, st.clientSeq, 0x17)

// 构建完整 TLS 记录层:类型(1) || 版本(2) || 长度(2) || 密文
sendRecordBuf := bytes.NewBuffer(nil)
sendRecordBuf.WriteByte(0x17) // Application Data 类型
sendRecordBuf.Write([]byte{0x03, 0x03}) // TLS 1.2
sendRecordBuf.Write([]byte{byte(len(sendEncrypted) >> 8), byte(len(sendEncrypted))}) // 长度
sendRecordBuf.Write(sendEncrypted) // 密文

// 直接发送已构建的记录层(避免 sendRecord 内部再次加密和打印)
if _, err := conn.Write(sendRecordBuf.Bytes()); err != nil {
panic(err)
}
st.clientSeq++
fmt.Printf(">>> Application Data 发送成功 (seq=%d)\n", st.clientSeq-1)
fmt.Println("============================================")

// ====== 步骤 13: 接收 HTTP 响应 ======
// 循环读取服务器返回的 HTTP 响应数据
fmt.Println("========== 接收的 Application Data ==========")
for {
contentType, _, _, err := recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq)
if err != nil {
if err.Error() == "EOF" || strings.Contains(err.Error(), "EOF") {
break
}
fmt.Printf(" Recv error: %v\n", err)
break
}
// 如果收到 Alert,说明服务器返回了错误
if contentType == 0x15 {
fmt.Println("<<< Alert received during HTTP")
break
}
// Application Data (0x17):HTTP 响应数据
if contentType == 0x17 {
st.serverSeq++
}
}
fmt.Println("============================================")
}