Skip to content

Go 加密与安全教程

📖 关于本教程本教程系统讲解密码学核心概念与 Go 实现:从自研加密算法理解原理,到工业级对称/非对称加密、哈希、数字签名、TLS,配合大量完整可运行的代码示例。


1. 自研对称加密算法

❌ 生产环境永远不要使用自研加密算法!本节仅为理解加密原理。自研算法无法抵抗密码分析攻击。实际项目请使用 AES、ChaCha20 等经过数十年验证的标准算法。

1.1 最简单的加密:XOR(异或)

go
package main

import (
    "fmt"
)

// ==================== XOR 加密 ====================
// 核心原理:A ^ B ^ B = A
// 明文 XOR 密钥 = 密文
// 密文 XOR 密钥 = 明文(同一操作,加解密完全相同)

func xorEncrypt(data, key []byte) []byte {
    result := make([]byte, len(data))
    keyLen := len(key)
    for i, b := range data {
        result[i] = b ^ key[i%keyLen] // 密钥循环使用
    }
    return result
}

func main() {
    plaintext := []byte("Hello, 密码学!")
    key := []byte("mysecretkey")

    // 加密
    ciphertext := xorEncrypt(plaintext, key)
    fmt.Printf("明文: %s\n", plaintext)
    fmt.Printf("密文 (hex): %x\n", ciphertext)

    // 解密(同样的操作)
    decrypted := xorEncrypt(ciphertext, key)
    fmt.Printf("解密: %s\n", decrypted)
}

1.2 增强版:多轮替换 + 置换

go
package main

import (
    "fmt"
)

// SimpleBlockCipher 自研分组加密(仅供学习)
// 块大小 8 字节,密钥 8 字节,4 轮迭代
type SimpleBlockCipher struct {
    key    [8]byte
    sbox   [256]byte // 替换盒(Substitution Box)
    sboxInv [256]byte // 逆替换盒
}

func NewSimpleBlockCipher(key []byte) *SimpleBlockCipher {
    c := &SimpleBlockCipher{}
    copy(c.key[:], key)

    // 基于密钥生成 S-Box(替换表)
    // 实际算法中 S-Box 的设计极其考究(抗差分攻击、抗线性攻击)
    for i := 0; i < 256; i++ {
        c.sbox[i] = byte(i)
    }
    j := byte(0)
    for i := 0; i < 256; i++ {
        j = j + c.sbox[i] + key[i%len(key)]
        c.sbox[i], c.sbox[j] = c.sbox[j], c.sbox[i]
    }
    // 生成逆 S-Box
    for i := 0; i < 256; i++ {
        c.sboxInv[c.sbox[i]] = byte(i)
    }
    return c
}

// EncryptBlock 加密一个 8 字节块
func (c *SimpleBlockCipher) EncryptBlock(block [8]byte) [8]byte {
    state := block

    for round := 0; round < 4; round++ {
        // 第1步:轮密钥加(XOR with round key)
        roundKey := c.deriveRoundKey(round)
        for i := 0; i < 8; i++ {
            state[i] ^= roundKey[i]
        }

        // 第2步:字节替换(Substitution)
        for i := 0; i < 8; i++ {
            state[i] = c.sbox[state[i]]
        }

        // 第3步:字节置换(Permutation)—— 循环左移
        state = [8]byte{
            state[1], state[3], state[5], state[7],
            state[0], state[2], state[4], state[6],
        }
    }

    return state
}

// DecryptBlock 解密一个 8 字节块(逆操作,逆序执行)
func (c *SimpleBlockCipher) DecryptBlock(block [8]byte) [8]byte {
    state := block

    for round := 3; round >= 0; round-- {
        // 逆置换(循环右移)
        state = [8]byte{
            state[4], state[0], state[5], state[1],
            state[6], state[2], state[7], state[3],
        }

        // 逆替换
        for i := 0; i < 8; i++ {
            state[i] = c.sboxInv[state[i]]
        }

        // 逆轮密钥加(XOR 的逆就是 XOR 本身)
        roundKey := c.deriveRoundKey(round)
        for i := 0; i < 8; i++ {
            state[i] ^= roundKey[i]
        }
    }

    return state
}

// deriveRoundKey 从主密钥派生每轮的子密钥
func (c *SimpleBlockCipher) deriveRoundKey(round int) [8]byte {
    var roundKey [8]byte
    for i := 0; i < 8; i++ {
        roundKey[i] = c.key[(i+round)%8] ^ byte(round*0x37+i*0x13)
    }
    return roundKey
}

func main() {
    key := []byte("12345678")
    cipher := NewSimpleBlockCipher(key)

    // 加密
    plainBlock := [8]byte{'H', 'e', 'l', 'l', 'o', '!', '!', '!'}
    encrypted := cipher.EncryptBlock(plainBlock)
    fmt.Printf("明文: %s\n", plainBlock[:])
    fmt.Printf("密文: %x\n", encrypted)

    // 解密
    decrypted := cipher.DecryptBlock(encrypted)
    fmt.Printf("解密: %s\n", decrypted[:])

    // 验证
    if decrypted == plainBlock {
        fmt.Println("✅ 加解密一致")
    }
}

1.3 对称加密的基本结构

text
现代对称加密算法的通用结构(SPN / Feistel):

  明文(一个固定大小的块)


  ┌───────────────┐
  │ 轮密钥加       │  ← 子密钥 K0(XOR)
  ├───────────────┤
  │ 字节替换       │  ← S-Box(非线性变换,提供混淆)
  ├───────────────┤
  │ 行/列置换     │  ← P-Box(线性变换,提供扩散)
  ├───────────────┤
  │ 列混合        │  ← MixColumns(AES 特有)
  └───────────────┘
    │ 重复 N 轮

  密文

关键概念:
  - 混淆(Confusion):密文和密钥的关系尽量复杂(S-Box 实现)
  - 扩散(Diffusion):明文的一个 bit 变化影响密文的多个 bit(P-Box 实现)
  - 雪崩效应:输入变 1 bit,输出变 ~50% 的 bit

AES-128:块大小 16 字节,密钥 16 字节,10 轮
AES-256:块大小 16 字节,密钥 32 字节,14 轮

2. 自行实现 CBC 分组加解密

2.1 分组模式是什么

text
分组加密一次只能处理固定大小的块(如 AES 的 16 字节)。
如果数据超过一个块怎么办?需要「分组模式」来组织多个块的加密。

常见分组模式:

  ECB(Electronic Codebook)—— 最简单但最不安全
    每个块独立加密
    相同明文块 → 相同密文块(泄露模式信息)
    ❌ 永远不要用于加密

  CBC(Cipher Block Chaining)—— 最常用
    每个块加密前先和前一个密文块 XOR
    第一个块和 IV(初始化向量)XOR
    相同明文块 → 不同密文块 ✅

  CTR(Counter)—— 流式加密
    将分组加密变成流加密
    加密一个递增计数器,用输出和明文 XOR
    可以并行加密 ✅

  GCM(Galois/Counter Mode)—— 推荐
    CTR + 认证标签(AEAD)
    同时提供加密和完整性验证 ✅✅

2.2 CBC 模式实现

go
package main

import (
    "bytes"
    "crypto/rand"
    "errors"
    "fmt"
)

// ==================== PKCS7 填充 ====================
// CBC 要求数据是块大小的整数倍,不足需要填充

func pkcs7Pad(data []byte, blockSize int) []byte {
    padding := blockSize - len(data)%blockSize
    padBytes := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(data, padBytes...)
}

func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
    if len(data) == 0 || len(data)%blockSize != 0 {
        return nil, errors.New("invalid padded data")
    }
    padding := int(data[len(data)-1])
    if padding == 0 || padding > blockSize {
        return nil, errors.New("invalid padding value")
    }
    // 验证所有填充字节
    for i := len(data) - padding; i < len(data); i++ {
        if data[i] != byte(padding) {
            return nil, errors.New("invalid padding bytes")
        }
    }
    return data[:len(data)-padding], nil
}

// ==================== 块加密接口 ====================
// 抽象出块加密,CBC 不关心具体用什么算法加密每个块

type BlockCipher interface {
    BlockSize() int
    EncryptBlock(dst, src []byte) // 加密一个块
    DecryptBlock(dst, src []byte) // 解密一个块
}

// ==================== 简单的 XOR 块加密(演示用)====================
type XORBlockCipher struct {
    key []byte
}

func NewXORBlockCipher(key []byte) *XORBlockCipher {
    return &XORBlockCipher{key: key}
}

func (c *XORBlockCipher) BlockSize() int { return len(c.key) }

func (c *XORBlockCipher) EncryptBlock(dst, src []byte) {
    for i := 0; i < len(c.key); i++ {
        // 多轮 XOR + 替换模拟加密
        dst[i] = (src[i] ^ c.key[i]) + c.key[(i+1)%len(c.key)]
    }
}

func (c *XORBlockCipher) DecryptBlock(dst, src []byte) {
    for i := 0; i < len(c.key); i++ {
        dst[i] = (src[i] - c.key[(i+1)%len(c.key)]) ^ c.key[i]
    }
}

// ==================== CBC 加密 ====================
//
// CBC 加密流程:
//   C0 = Encrypt(P0 XOR IV)
//   C1 = Encrypt(P1 XOR C0)
//   C2 = Encrypt(P2 XOR C1)
//   ...
//
// IV 必须随机生成,和密文一起传输

func CBCEncrypt(cipher BlockCipher, plaintext, iv []byte) ([]byte, error) {
    blockSize := cipher.BlockSize()

    if len(iv) != blockSize {
        return nil, fmt.Errorf("IV length must be %d", blockSize)
    }

    // PKCS7 填充
    padded := pkcs7Pad(plaintext, blockSize)

    ciphertext := make([]byte, len(padded))
    prevBlock := iv // 第一个块使用 IV

    for i := 0; i < len(padded); i += blockSize {
        // 当前明文块
        block := padded[i : i+blockSize]

        // XOR with previous ciphertext block (or IV)
        xored := make([]byte, blockSize)
        for j := 0; j < blockSize; j++ {
            xored[j] = block[j] ^ prevBlock[j]
        }

        // 加密
        cipher.EncryptBlock(ciphertext[i:i+blockSize], xored)

        // 当前密文块成为下一轮的 prevBlock
        prevBlock = ciphertext[i : i+blockSize]
    }

    return ciphertext, nil
}

// ==================== CBC 解密 ====================
//
// CBC 解密流程:
//   P0 = Decrypt(C0) XOR IV
//   P1 = Decrypt(C1) XOR C0
//   P2 = Decrypt(C2) XOR C1
//   ...

func CBCDecrypt(cipher BlockCipher, ciphertext, iv []byte) ([]byte, error) {
    blockSize := cipher.BlockSize()

    if len(ciphertext)%blockSize != 0 {
        return nil, errors.New("ciphertext is not a multiple of block size")
    }

    plaintext := make([]byte, len(ciphertext))
    prevBlock := iv

    for i := 0; i < len(ciphertext); i += blockSize {
        // 解密当前块
        decrypted := make([]byte, blockSize)
        cipher.DecryptBlock(decrypted, ciphertext[i:i+blockSize])

        // XOR with previous ciphertext block (or IV)
        for j := 0; j < blockSize; j++ {
            plaintext[i+j] = decrypted[j] ^ prevBlock[j]
        }

        prevBlock = ciphertext[i : i+blockSize]
    }

    // 去除填充
    return pkcs7Unpad(plaintext, blockSize)
}

func main() {
    key := []byte("16bytesecretkey!") // 16 字节密钥
    cipher := NewXORBlockCipher(key)

    // 随机生成 IV(每次加密必须不同!)
    iv := make([]byte, cipher.BlockSize())
    rand.Read(iv)

    plaintext := []byte("这是一段需要加密的敏感信息,长度超过一个块")
    fmt.Printf("明文: %s\n", plaintext)
    fmt.Printf("明文长度: %d 字节\n", len(plaintext))

    // 加密
    ciphertext, err := CBCEncrypt(cipher, plaintext, iv)
    if err != nil {
        fmt.Println("加密失败:", err)
        return
    }
    fmt.Printf("密文 (hex): %x\n", ciphertext)

    // 解密
    decrypted, err := CBCDecrypt(cipher, ciphertext, iv)
    if err != nil {
        fmt.Println("解密失败:", err)
        return
    }
    fmt.Printf("解密: %s\n", decrypted)

    // 验证
    if bytes.Equal(plaintext, decrypted) {
        fmt.Println("✅ CBC 加解密一致")
    }

    // ==================== 演示 CBC 的雪崩效应 ====================
    fmt.Println("\n=== 修改一个字节看密文变化 ===")
    plaintext2 := make([]byte, len(plaintext))
    copy(plaintext2, plaintext)
    plaintext2[0] ^= 0x01 // 改变第一个字节的 1 bit

    ciphertext2, _ := CBCEncrypt(cipher, plaintext2, iv)
    diffCount := 0
    for i := 0; i < len(ciphertext) && i < len(ciphertext2); i++ {
        if ciphertext[i] != ciphertext2[i] {
            diffCount++
        }
    }
    fmt.Printf("密文不同字节数: %d / %d%.0f%%\n",
        diffCount, len(ciphertext),
        float64(diffCount)/float64(len(ciphertext))*100)
}

3. 对称加密

3.1 AES-GCM(推荐方案)

go
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "io"
)

// ==================== AES-GCM:认证加密(AEAD)====================
// GCM = Galois/Counter Mode
// 同时提供:加密 + 完整性验证 + 防篡改
// 是目前最推荐的对称加密模式

// AESGCMEncrypt AES-GCM 加密
func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) {
    // 创建 AES cipher
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, fmt.Errorf("new cipher: %w", err)
    }

    // 创建 GCM 模式
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, fmt.Errorf("new gcm: %w", err)
    }

    // 生成随机 nonce(GCM 的 IV,通常 12 字节)
    nonce := make([]byte, gcm.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return nil, fmt.Errorf("generate nonce: %w", err)
    }

    // 加密(nonce 拼接在密文前面,方便解密时取出)
    // Seal 的输出 = nonce + 密文 + 认证标签(16 字节)
    ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
    return ciphertext, nil
}

// AESGCMDecrypt AES-GCM 解密
func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, fmt.Errorf("new cipher: %w", err)
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, fmt.Errorf("new gcm: %w", err)
    }

    nonceSize := gcm.NonceSize()
    if len(ciphertext) < nonceSize {
        return nil, fmt.Errorf("ciphertext too short")
    }

    // 分离 nonce 和实际密文
    nonce := ciphertext[:nonceSize]
    encryptedData := ciphertext[nonceSize:]

    // 解密(同时验证完整性,被篡改会返回 error)
    plaintext, err := gcm.Open(nil, nonce, encryptedData, nil)
    if err != nil {
        return nil, fmt.Errorf("decrypt failed (data may be tampered): %w", err)
    }

    return plaintext, nil
}

func main() {
    // AES-128: 16 字节密钥
    // AES-192: 24 字节密钥
    // AES-256: 32 字节密钥
    key := make([]byte, 32) // AES-256
    rand.Read(key)

    plaintext := []byte("这是机密信息:账号 admin,密码 P@ssw0rd")
    fmt.Printf("明文: %s\n", plaintext)

    // 加密
    ciphertext, err := AESGCMEncrypt(key, plaintext)
    if err != nil {
        fmt.Println("加密失败:", err)
        return
    }
    fmt.Printf("密文 (hex): %s\n", hex.EncodeToString(ciphertext))
    fmt.Printf("密文长度: %d(明文 %d + nonce 12 + tag 16)\n",
        len(ciphertext), len(plaintext))

    // 解密
    decrypted, err := AESGCMDecrypt(key, ciphertext)
    if err != nil {
        fmt.Println("解密失败:", err)
        return
    }
    fmt.Printf("解密: %s\n", decrypted)

    // ==================== 验证防篡改 ====================
    fmt.Println("\n=== 篡改测试 ===")
    tampered := make([]byte, len(ciphertext))
    copy(tampered, ciphertext)
    tampered[20] ^= 0xFF // 篡改一个字节

    _, err = AESGCMDecrypt(key, tampered)
    fmt.Println("篡改后解密:", err) // 会报错:message authentication failed
}

3.2 AES-CBC(传统方案)

go
package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "fmt"
    "io"
)

// AESCBCEncrypt AES-CBC 加密
func AESCBCEncrypt(key, plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    // PKCS7 填充
    blockSize := block.BlockSize()
    padding := blockSize - len(plaintext)%blockSize
    padded := append(plaintext, bytes.Repeat([]byte{byte(padding)}, padding)...)

    // 随机 IV
    ciphertext := make([]byte, blockSize+len(padded)) // IV + 密文
    iv := ciphertext[:blockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    // CBC 加密
    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext[blockSize:], padded)

    return ciphertext, nil // IV 拼在密文前面
}

// AESCBCDecrypt AES-CBC 解密
func AESCBCDecrypt(key, ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize()
    if len(ciphertext) < blockSize*2 {
        return nil, fmt.Errorf("ciphertext too short")
    }

    // 分离 IV 和密文
    iv := ciphertext[:blockSize]
    data := ciphertext[blockSize:]

    // CBC 解密
    mode := cipher.NewCBCDecrypter(block, iv)
    plaintext := make([]byte, len(data))
    mode.CryptBlocks(plaintext, data)

    // 去除 PKCS7 填充
    padding := int(plaintext[len(plaintext)-1])
    if padding > blockSize || padding == 0 {
        return nil, fmt.Errorf("invalid padding")
    }
    return plaintext[:len(plaintext)-padding], nil
}

func main() {
    key := make([]byte, 32) // AES-256
    rand.Read(key)

    plaintext := []byte("AES-CBC 加密示例")

    ciphertext, _ := AESCBCEncrypt(key, plaintext)
    decrypted, _ := AESCBCDecrypt(key, ciphertext)

    fmt.Printf("明文: %s\n", plaintext)
    fmt.Printf("密文: %x\n", ciphertext)
    fmt.Printf("解密: %s\n", decrypted)
}

⚠️ GCM vs CBC CBC 只提供加密,不提供完整性验证。攻击者可以篡改密文而不被检测(Padding Oracle Attack)。GCM 同时提供加密和认证,篡改密文会被发现。新项目请用 AES-GCM


4. 练习:文件加密

💡 要求实现文件加密工具:支持大文件流式加密/解密,使用 AES-256-CTR 模式,密钥由密码通过 Argon2 派生。加密文件格式包含 salt 和 nonce,可以自解密。

go
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "crypto/sha256"
    "errors"
    "fmt"
    "io"
    "os"

    "golang.org/x/crypto/argon2"
)

// ==================== 加密文件格式 ====================
// ┌──────────┬──────────┬──────────┬─────────────────┐
// │ Magic    │ Salt     │ Nonce    │ Encrypted Data  │
// │ 8 bytes  │ 32 bytes │ 12 bytes │ 变长            │
// │ "GOENCR1"│ 随机     │ 随机     │ CTR 流式加密     │
// └──────────┴──────────┴──────────┴─────────────────┘

var fileMagic = []byte("GOENCR1\x00")

const (
    saltSize  = 32
    nonceSize = 12
)

// deriveKey 从密码派生 AES-256 密钥(使用 Argon2id)
func deriveKey(password string, salt []byte) []byte {
    // Argon2id 参数:时间 3 次,内存 64MB,并行度 4
    return argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)
}

// EncryptFile 加密文件(流式,支持大文件)
func EncryptFile(srcPath, dstPath, password string) error {
    // 打开源文件
    src, err := os.Open(srcPath)
    if err != nil {
        return fmt.Errorf("open source: %w", err)
    }
    defer src.Close()

    // 创建目标文件
    dst, err := os.Create(dstPath)
    if err != nil {
        return fmt.Errorf("create dest: %w", err)
    }
    defer dst.Close()

    // 生成随机 salt 和 nonce
    salt := make([]byte, saltSize)
    if _, err := rand.Read(salt); err != nil {
        return fmt.Errorf("generate salt: %w", err)
    }
    nonce := make([]byte, nonceSize)
    if _, err := rand.Read(nonce); err != nil {
        return fmt.Errorf("generate nonce: %w", err)
    }

    // 从密码派生密钥
    key := deriveKey(password, salt)

    // 创建 AES-CTR 流
    block, err := aes.NewCipher(key)
    if err != nil {
        return fmt.Errorf("new cipher: %w", err)
    }
    stream := cipher.NewCTR(block, append(nonce, make([]byte, aes.BlockSize-nonceSize)...))

    // 写入文件头:magic + salt + nonce
    dst.Write(fileMagic)
    dst.Write(salt)
    dst.Write(nonce)

    // 流式加密(每次处理 32KB,不会把整个文件读入内存)
    buf := make([]byte, 32*1024)
    for {
        n, err := src.Read(buf)
        if n > 0 {
            encrypted := make([]byte, n)
            stream.XORKeyStream(encrypted, buf[:n])
            if _, err := dst.Write(encrypted); err != nil {
                return fmt.Errorf("write encrypted: %w", err)
            }
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return fmt.Errorf("read source: %w", err)
        }
    }

    // 计算并附加校验和(用于验证密码是否正确)
    src.Seek(0, 0)
    hash := sha256.New()
    io.Copy(hash, src)
    checksum := hash.Sum(nil)[:8] // 取前 8 字节作为校验

    // 加密校验和并写入
    encChecksum := make([]byte, 8)
    stream.XORKeyStream(encChecksum, checksum)
    dst.Write(encChecksum)

    return nil
}

// DecryptFile 解密文件
func DecryptFile(srcPath, dstPath, password string) error {
    src, err := os.Open(srcPath)
    if err != nil {
        return fmt.Errorf("open source: %w", err)
    }
    defer src.Close()

    // 读取文件头
    magic := make([]byte, 8)
    if _, err := io.ReadFull(src, magic); err != nil {
        return errors.New("invalid file format")
    }
    if string(magic) != string(fileMagic) {
        return errors.New("not an encrypted file")
    }

    salt := make([]byte, saltSize)
    if _, err := io.ReadFull(src, salt); err != nil {
        return errors.New("read salt failed")
    }
    nonce := make([]byte, nonceSize)
    if _, err := io.ReadFull(src, nonce); err != nil {
        return errors.New("read nonce failed")
    }

    // 派生密钥
    key := deriveKey(password, salt)

    // 创建解密流
    block, err := aes.NewCipher(key)
    if err != nil {
        return err
    }
    stream := cipher.NewCTR(block, append(nonce, make([]byte, aes.BlockSize-nonceSize)...))

    // 创建目标文件
    dst, err := os.Create(dstPath)
    if err != nil {
        return fmt.Errorf("create dest: %w", err)
    }
    defer dst.Close()

    // 流式解密
    buf := make([]byte, 32*1024)
    for {
        n, err := src.Read(buf)
        if n > 0 {
            decrypted := make([]byte, n)
            stream.XORKeyStream(decrypted, buf[:n])
            dst.Write(decrypted)
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return fmt.Errorf("read: %w", err)
        }
    }

    return nil
}

func main() {
    password := "my-secret-password-123"

    // 创建测试文件
    testData := []byte("这是一个敏感文件的内容,包含重要的商业机密...\n")
    for i := 0; i < 10; i++ {
        testData = append(testData, []byte(fmt.Sprintf("第 %d 行数据\n", i))...)
    }
    os.WriteFile("secret.txt", testData, 0644)

    // 加密
    fmt.Println("加密中...")
    if err := EncryptFile("secret.txt", "secret.txt.enc", password); err != nil {
        fmt.Println("加密失败:", err)
        return
    }

    origInfo, _ := os.Stat("secret.txt")
    encInfo, _ := os.Stat("secret.txt.enc")
    fmt.Printf("原始文件: %d 字节\n", origInfo.Size())
    fmt.Printf("加密文件: %d 字节(多了 %d 字节头部)\n",
        encInfo.Size(), encInfo.Size()-origInfo.Size())

    // 解密
    fmt.Println("\n解密中...")
    if err := DecryptFile("secret.txt.enc", "secret_decrypted.txt", password); err != nil {
        fmt.Println("解密失败:", err)
        return
    }

    // 验证
    original, _ := os.ReadFile("secret.txt")
    decrypted, _ := os.ReadFile("secret_decrypted.txt")
    if string(original) == string(decrypted) {
        fmt.Println("✅ 文件解密验证通过")
    } else {
        fmt.Println("❌ 文件内容不一致")
    }

    // 错误密码测试
    fmt.Println("\n=== 错误密码测试 ===")
    err := DecryptFile("secret.txt.enc", "wrong.txt", "wrong-password")
    if err != nil {
        fmt.Println("错误密码:", err)
    } else {
        fmt.Println("解密完成但内容是乱码(CTR 模式不报错,需要校验和验证)")
    }

    // 清理
    os.Remove("secret.txt")
    os.Remove("secret.txt.enc")
    os.Remove("secret_decrypted.txt")
    os.Remove("wrong.txt")
}

5. 非对称加密

5.1 非对称加密原理

text
非对称加密(公钥加密):
  - 一对密钥:公钥(public key)和私钥(private key)
  - 公钥加密 → 只有私钥能解密
  - 私钥签名 → 公钥验证签名

  核心特性:
    公钥可以公开发布(任何人都能加密 / 验签)
    私钥必须保密(只有持有者能解密 / 签名)

  数学基础:
    RSA:大数分解难题(两个大素数的乘积容易算,反过来分解极难)
    ECDSA/Ed25519:椭圆曲线离散对数难题

  对比:
    RSA-2048:密钥大 (256 字节),加密慢,兼容性好
    Ed25519:密钥小 (32 字节),速度快,更安全,现代推荐

5.2 RSA 加密

go
package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "os"
)

// GenerateRSAKeys 生成 RSA 密钥对
func GenerateRSAKeys(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
    privateKey, err := rsa.GenerateKey(rand.Reader, bits)
    if err != nil {
        return nil, nil, err
    }
    return privateKey, &privateKey.PublicKey, nil
}

// SavePrivateKeyPEM 保存私钥为 PEM 文件
func SavePrivateKeyPEM(key *rsa.PrivateKey, path string) error {
    keyBytes := x509.MarshalPKCS1PrivateKey(key)
    block := &pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: keyBytes,
    }
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    defer file.Close()
    return pem.Encode(file, block)
}

// SavePublicKeyPEM 保存公钥为 PEM 文件
func SavePublicKeyPEM(key *rsa.PublicKey, path string) error {
    keyBytes, err := x509.MarshalPKIXPublicKey(key)
    if err != nil {
        return err
    }
    block := &pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: keyBytes,
    }
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    defer file.Close()
    return pem.Encode(file, block)
}

func main() {
    // 生成 2048 位 RSA 密钥对
    privateKey, publicKey, err := GenerateRSAKeys(2048)
    if err != nil {
        fmt.Println("生成密钥失败:", err)
        return
    }

    // 保存密钥
    SavePrivateKeyPEM(privateKey, "private.pem")
    SavePublicKeyPEM(publicKey, "public.pem")
    fmt.Println("密钥已保存到 private.pem 和 public.pem")

    // ==================== RSA-OAEP 加密 ====================
    // OAEP = Optimal Asymmetric Encryption Padding(推荐)
    plaintext := []byte("RSA 加密的机密信息")

    ciphertext, err := rsa.EncryptOAEP(
        sha256.New(),   // 哈希算法
        rand.Reader,    // 随机源
        publicKey,      // 用公钥加密
        plaintext,
        nil,            // label(可选附加数据)
    )
    if err != nil {
        fmt.Println("加密失败:", err)
        return
    }
    fmt.Printf("密文长度: %d 字节\n", len(ciphertext))

    // ==================== RSA-OAEP 解密 ====================
    decrypted, err := rsa.DecryptOAEP(
        sha256.New(),
        rand.Reader,
        privateKey, // 用私钥解密
        ciphertext,
        nil,
    )
    if err != nil {
        fmt.Println("解密失败:", err)
        return
    }
    fmt.Printf("解密: %s\n", decrypted)

    // ⚠️ RSA 加密有长度限制:明文 ≤ 密钥长度 - 填充开销
    // RSA-2048 + SHA-256 OAEP:最多加密 190 字节
    // 解决:RSA 加密一个 AES 密钥,再用 AES 加密实际数据(混合加密)

    // 清理
    os.Remove("private.pem")
    os.Remove("public.pem")
}

5.3 Ed25519(现代推荐)

go
package main

import (
    "crypto/ed25519"
    "crypto/rand"
    "encoding/hex"
    "fmt"
)

func main() {
    // Ed25519 主要用于签名,不用于加密
    // 如果需要非对称加密,可用 X25519 密钥交换 + AES

    // 生成密钥对
    publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
    if err != nil {
        fmt.Println("生成密钥失败:", err)
        return
    }

    fmt.Printf("公钥 (%d bytes): %s\n", len(publicKey), hex.EncodeToString(publicKey))
    fmt.Printf("私钥 (%d bytes): %s...\n", len(privateKey), hex.EncodeToString(privateKey[:16]))

    // 签名
    message := []byte("这条消息需要签名验证")
    signature := ed25519.Sign(privateKey, message)
    fmt.Printf("签名 (%d bytes): %s\n", len(signature), hex.EncodeToString(signature))

    // 验签
    valid := ed25519.Verify(publicKey, message, signature)
    fmt.Println("签名验证:", valid) // true

    // 篡改消息后验签
    tampered := []byte("这条消息被篡改了")
    valid2 := ed25519.Verify(publicKey, tampered, signature)
    fmt.Println("篡改后验证:", valid2) // false
}

6. 哈希算法

go
package main

import (
    "crypto/md5"
    "crypto/sha256"
    "crypto/sha512"
    "encoding/hex"
    "fmt"
    "hash"

    "golang.org/x/crypto/sha3"
)

func computeHash(name string, h hash.Hash, data []byte) {
    h.Write(data)
    result := h.Sum(nil)
    fmt.Printf("%-10s (%3d bit): %s\n", name, len(result)*8, hex.EncodeToString(result))
}

func main() {
    data := []byte("Hello, 哈希算法!")

    fmt.Println("=== 常用哈希算法对比 ===")
    fmt.Printf("输入: %s\n\n", data)

    computeHash("MD5", md5.New(), data)             // 128 bit(已不安全,仅用于校验)
    computeHash("SHA-256", sha256.New(), data)       // 256 bit(推荐)
    computeHash("SHA-384", sha512.New384(), data)    // 384 bit
    computeHash("SHA-512", sha512.New(), data)       // 512 bit
    computeHash("SHA3-256", sha3.New256(), data)     // 256 bit(新标准)

    // ==================== 快捷方式 ====================
    hash1 := sha256.Sum256(data) // 返回 [32]byte
    fmt.Printf("\n快捷方式: %x\n", hash1)

    // ==================== 雪崩效应演示 ====================
    fmt.Println("\n=== 雪崩效应 ===")
    data1 := []byte("Hello")
    data2 := []byte("Hellp") // 只差一个字母

    hash1Sum := sha256.Sum256(data1)
    hash2Sum := sha256.Sum256(data2)

    diffBits := 0
    for i := 0; i < 32; i++ {
        xor := hash1Sum[i] ^ hash2Sum[i]
        for xor > 0 {
            diffBits += int(xor & 1)
            xor >>= 1
        }
    }
    fmt.Printf("输入差异: 1 字节\n")
    fmt.Printf("输出差异: %d / 256 bit (%.1f%%)\n", diffBits, float64(diffBits)/256*100)

    // ==================== 哈希用途总结 ====================
    fmt.Println(`
=== 哈希算法选择指南 ===
场景                   推荐算法
─────────────────────────────────
文件完整性校验          SHA-256
密码存储              bcrypt / argon2(不要用 SHA)
数字签名              SHA-256 或 SHA-384
区块链                SHA-256 / SHA3-256 / Keccak
HMAC 消息认证          HMAC-SHA256
快速校验(非安全)      MD5 / CRC32
数据去重              SHA-256`)
}

❌ 绝对不要用 MD5/SHA 直接存储密码!哈希不等于加密。MD5/SHA 速度太快,攻击者可以暴力穷举。存储密码必须用专门的密码哈希函数:bcryptargon2scrypt,它们故意设计得很慢。


7. 练习:对超大文件执行哈希

💡 要求计算一个几 GB 大文件的 SHA-256 哈希值,不能把整个文件读入内存。同时显示进度。

go
package main

import (
    "crypto/sha256"
    "fmt"
    "hash"
    "io"
    "os"
    "time"
)

// ProgressReader 带进度回调的 Reader 包装
type ProgressReader struct {
    reader    io.Reader
    total     int64 // 总大小
    read      int64 // 已读取
    callback  func(read, total int64)
    lastCall  time.Time
}

func NewProgressReader(r io.Reader, total int64, cb func(int64, int64)) *ProgressReader {
    return &ProgressReader{
        reader:   r,
        total:    total,
        callback: cb,
        lastCall: time.Now(),
    }
}

func (pr *ProgressReader) Read(p []byte) (int, error) {
    n, err := pr.reader.Read(p)
    pr.read += int64(n)

    // 每 100ms 更新一次进度(避免频繁回调)
    if time.Since(pr.lastCall) > 100*time.Millisecond || err == io.EOF {
        pr.callback(pr.read, pr.total)
        pr.lastCall = time.Now()
    }
    return n, err
}

// HashFile 计算文件哈希(流式,支持超大文件)
func HashFile(path string, h hash.Hash) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    // 获取文件大小
    info, err := file.Stat()
    if err != nil {
        return nil, err
    }

    // 带进度的读取
    start := time.Now()
    pr := NewProgressReader(file, info.Size(), func(read, total int64) {
        pct := float64(read) / float64(total) * 100
        speed := float64(read) / time.Since(start).Seconds() / 1024 / 1024
        fmt.Printf("\r  进度: %.1f%% (%d / %d bytes) 速度: %.1f MB/s",
            pct, read, total, speed)
    })

    // io.Copy 内部使用 32KB 缓冲区,不会吃内存
    if _, err := io.Copy(h, pr); err != nil {
        return nil, err
    }

    fmt.Println() // 换行
    return h.Sum(nil), nil
}

// HashFileConcurrent 并发分段哈希(利用多核加速)
// 注意:这不等于标准 SHA-256,而是分段哈希后再合并
// 标准哈希必须顺序计算,这里仅作为自定义校验方案的示例
func HashFileConcurrent(path string, workers int) ([]byte, error) {
    info, err := os.Stat(path)
    if err != nil {
        return nil, err
    }

    fileSize := info.Size()
    chunkSize := fileSize / int64(workers)

    type chunkHash struct {
        index int
        hash  [32]byte
    }

    results := make(chan chunkHash, workers)

    for i := 0; i < workers; i++ {
        go func(idx int) {
            f, _ := os.Open(path)
            defer f.Close()

            offset := int64(idx) * chunkSize
            size := chunkSize
            if idx == workers-1 {
                size = fileSize - offset // 最后一段取剩余
            }

            f.Seek(offset, 0)
            h := sha256.New()
            io.CopyN(h, f, size)

            var hash [32]byte
            copy(hash[:], h.Sum(nil))
            results <- chunkHash{index: idx, hash: hash}
        }(i)
    }

    // 收集并按顺序合并
    hashes := make([][32]byte, workers)
    for i := 0; i < workers; i++ {
        r := <-results
        hashes[r.index] = r.hash
    }

    // 合并所有分段哈希
    final := sha256.New()
    for _, h := range hashes {
        final.Write(h[:])
    }

    return final.Sum(nil), nil
}

func main() {
    // 创建测试文件(100MB)
    testFile := "testfile_large.bin"
    fmt.Println("创建 100MB 测试文件...")
    createLargeFile(testFile, 100*1024*1024)

    // 标准顺序哈希
    fmt.Println("\n=== 标准 SHA-256 ===")
    start := time.Now()
    hash1, err := HashFile(testFile, sha256.New())
    if err != nil {
        fmt.Println("哈希失败:", err)
        return
    }
    fmt.Printf("  SHA-256: %x\n", hash1)
    fmt.Printf("  耗时: %s\n", time.Since(start))

    // 并发分段哈希
    fmt.Println("\n=== 并发分段哈希 (4 workers) ===")
    start = time.Now()
    hash2, err := HashFileConcurrent(testFile, 4)
    if err != nil {
        fmt.Println("并发哈希失败:", err)
        return
    }
    fmt.Printf("  分段哈希: %x\n", hash2)
    fmt.Printf("  耗时: %s\n", time.Since(start))

    // 清理
    os.Remove(testFile)
}

func createLargeFile(path string, size int) {
    f, _ := os.Create(path)
    defer f.Close()

    buf := make([]byte, 1024*1024) // 1MB 缓冲区
    for i := range buf {
        buf[i] = byte(i % 256)
    }
    for written := 0; written < size; written += len(buf) {
        remaining := size - written
        if remaining < len(buf) {
            f.Write(buf[:remaining])
        } else {
            f.Write(buf)
        }
    }
}

8. 数字签名和 TLS 原理

8.1 数字签名

text
数字签名 = 用私钥对数据的哈希值加密

流程:
  签名方(持有私钥):
    1. 计算数据的哈希值:H = SHA256(data)
    2. 用私钥加密哈希值:signature = Sign(privateKey, H)
    3. 发送:data + signature

  验证方(持有公钥):
    1. 计算数据的哈希值:H' = SHA256(data)
    2. 用公钥验证签名:Verify(publicKey, H', signature)
    3. 如果验证通过:数据未被篡改 + 确实来自签名方

数字签名保证了:
  ✅ 完整性(数据未被篡改)
  ✅ 认证性(确认发送方身份)
  ✅ 不可否认性(签名方不能否认)
go
package main

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "fmt"
)

func main() {
    // 生成 RSA 密钥对
    privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
    publicKey := &privateKey.PublicKey

    message := []byte("这是一份合同:甲方同意支付 100 万元")

    // ==================== 签名(用私钥)====================
    hashed := sha256.Sum256(message)
    signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
    if err != nil {
        fmt.Println("签名失败:", err)
        return
    }
    fmt.Printf("消息: %s\n", message)
    fmt.Printf("签名 (%d bytes): %x...\n", len(signature), signature[:32])

    // ==================== 验签(用公钥)====================
    err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signature)
    if err != nil {
        fmt.Println("❌ 验签失败:", err)
    } else {
        fmt.Println("✅ 验签通过:消息完整且来自持有私钥的人")
    }

    // ==================== 篡改测试 ====================
    tampered := []byte("这是一份合同:甲方同意支付 1 元")
    tamperedHash := sha256.Sum256(tampered)
    err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, tamperedHash[:], signature)
    if err != nil {
        fmt.Println("✅ 篡改检测成功:", err)
    }
}

8.2 TLS 原理

text
TLS(Transport Layer Security)在 TCP 之上提供加密通信。

TLS 1.3 握手过程(简化):

  客户端                              服务端
    │                                  │
    │─── ClientHello ────────────────→│
    │    支持的密码套件                   │
    │    客户端随机数                     │
    │    密钥共享参数(ECDHE)            │
    │                                  │
    │←── ServerHello ────────────────│
    │    选定的密码套件                   │
    │    服务端随机数                     │
    │    密钥共享参数(ECDHE)            │
    │    服务端证书(含公钥)              │
    │    证书验证签名                    │
    │    Finished                      │
    │                                  │
    │   【此时双方用 ECDHE 计算出共享密钥】 │
    │                                  │
    │─── Finished ───────────────────→│
    │                                  │
    │←══ 加密通信开始 ══════════════════│
    │   (使用 AES-GCM + 共享密钥)      │

关键技术组合:
  1. ECDHE:密钥交换(双方在不安全信道上协商出共享密钥)
  2. RSA/Ed25519:数字签名(验证服务器身份)
  3. X.509 证书:公钥的可信载体(CA 签发)
  4. AES-GCM:对称加密(加密实际通信数据)
  5. HMAC:消息认证(确保完整性)

为什么需要这么多算法?
  - 非对称加密太慢,不能用来加密所有数据
  - 对称加密快,但双方怎么安全地交换密钥?
  - 答案:用非对称加密交换对称密钥(混合加密)

9. 练习:结合 socket 编程实现 TLS 的关键步骤

💡 目标不使用 crypto/tls 库,手动实现 TLS 的核心流程:ECDH 密钥交换 → 证书验证 → AES-GCM 加密通信。用于理解 TLS 原理,不用于生产环境。

9.1 ECDH 密钥交换

go
package main

import (
    "crypto/ecdh"
    "crypto/rand"
    "crypto/sha256"
    "fmt"
)

// ==================== ECDH 密钥交换 ====================
// 双方各自生成临时密钥对,交换公钥后计算出相同的共享密钥
// 即使攻击者截获公钥,也无法推算出共享密钥

func main() {
    curve := ecdh.X25519() // 使用 X25519 曲线

    // Alice 生成密钥对
    alicePrivate, _ := curve.GenerateKey(rand.Reader)
    alicePublic := alicePrivate.PublicKey()

    // Bob 生成密钥对
    bobPrivate, _ := curve.GenerateKey(rand.Reader)
    bobPublic := bobPrivate.PublicKey()

    fmt.Printf("Alice 公钥: %x\n", alicePublic.Bytes())
    fmt.Printf("Bob   公钥: %x\n", bobPublic.Bytes())

    // 交换公钥后,各自计算共享密钥
    // Alice: 用自己的私钥 + Bob 的公钥
    aliceShared, _ := alicePrivate.ECDH(bobPublic)
    // Bob: 用自己的私钥 + Alice 的公钥
    bobShared, _ := bobPrivate.ECDH(alicePublic)

    fmt.Printf("\nAlice 共享密钥: %x\n", aliceShared)
    fmt.Printf("Bob   共享密钥: %x\n", bobShared)

    // 两个共享密钥完全相同!
    if string(aliceShared) == string(bobShared) {
        fmt.Println("✅ 密钥交换成功:双方得到相同的共享密钥")
    }

    // 从共享密钥派生 AES 密钥
    aesKey := sha256.Sum256(aliceShared)
    fmt.Printf("\n派生 AES-256 密钥: %x\n", aesKey)
}

9.2 完整实现:手动 TLS 通信

go
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/ecdh"
    "crypto/ed25519"
    "crypto/rand"
    "crypto/sha256"
    "encoding/binary"
    "fmt"
    "io"
    "net"
    "sync"
    "time"
)

// ==================== 安全连接封装 ====================

// SecureConn 手动实现的加密连接
type SecureConn struct {
    conn   net.Conn
    gcm    cipher.AEAD
    mu     sync.Mutex
    readMu sync.Mutex
    nonce  uint64 // 递增 nonce,避免重复
}

// 从共享密钥创建加密连接
func newSecureConn(conn net.Conn, sharedKey []byte) (*SecureConn, error) {
    // 派生 AES-256 密钥
    derived := sha256.Sum256(sharedKey)

    block, err := aes.NewCipher(derived[:])
    if err != nil {
        return nil, err
    }
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    return &SecureConn{
        conn: conn,
        gcm:  gcm,
    }, nil
}

// Send 加密发送
func (sc *SecureConn) Send(plaintext []byte) error {
    sc.mu.Lock()
    defer sc.mu.Unlock()

    // 生成确定性 nonce(递增计数器,12 字节)
    sc.nonce++
    nonce := make([]byte, sc.gcm.NonceSize())
    binary.BigEndian.PutUint64(nonce[4:], sc.nonce)

    // GCM 加密
    ciphertext := sc.gcm.Seal(nil, nonce, plaintext, nil)

    // 发送:nonceLen(1) + nonce + dataLen(4) + data
    header := make([]byte, 1+len(nonce)+4)
    header[0] = byte(len(nonce))
    copy(header[1:], nonce)
    binary.BigEndian.PutUint32(header[1+len(nonce):], uint32(len(ciphertext)))

    if _, err := sc.conn.Write(header); err != nil {
        return err
    }
    _, err := sc.conn.Write(ciphertext)
    return err
}

// Recv 解密接收
func (sc *SecureConn) Recv() ([]byte, error) {
    sc.readMu.Lock()
    defer sc.readMu.Unlock()

    // 读取 nonce 长度
    nlBuf := make([]byte, 1)
    if _, err := io.ReadFull(sc.conn, nlBuf); err != nil {
        return nil, err
    }
    nonceLen := int(nlBuf[0])

    // 读取 nonce + 数据长度
    header := make([]byte, nonceLen+4)
    if _, err := io.ReadFull(sc.conn, header); err != nil {
        return nil, err
    }
    nonce := header[:nonceLen]
    dataLen := binary.BigEndian.Uint32(header[nonceLen:])

    // 读取密文
    ciphertext := make([]byte, dataLen)
    if _, err := io.ReadFull(sc.conn, ciphertext); err != nil {
        return nil, err
    }

    // GCM 解密(同时验证完整性)
    plaintext, err := sc.gcm.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return nil, fmt.Errorf("decrypt failed (tampered?): %w", err)
    }

    return plaintext, nil
}

func (sc *SecureConn) Close() error {
    return sc.conn.Close()
}

// ==================== 握手协议 ====================

// serverHandshake 服务端握手
func serverHandshake(conn net.Conn, serverSignKey ed25519.PrivateKey) (*SecureConn, error) {
    curve := ecdh.X25519()

    // 1. 生成临时 ECDH 密钥对
    serverPrivate, _ := curve.GenerateKey(rand.Reader)
    serverPublic := serverPrivate.PublicKey().Bytes()

    // 2. 接收客户端公钥(32 字节)
    clientPubBytes := make([]byte, 32)
    if _, err := io.ReadFull(conn, clientPubBytes); err != nil {
        return nil, fmt.Errorf("read client public key: %w", err)
    }

    // 3. 发送服务端公钥
    conn.Write(serverPublic)

    // 4. 发送服务端签名(证明身份)
    signData := append(clientPubBytes, serverPublic...)
    signature := ed25519.Sign(serverSignKey, signData)

    // 发送签名公钥(32 字节)+ 签名(64 字节)
    conn.Write(serverSignKey.Public().(ed25519.PublicKey))
    conn.Write(signature)

    // 5. 计算共享密钥
    clientPub, err := curve.NewPublicKey(clientPubBytes)
    if err != nil {
        return nil, fmt.Errorf("parse client public key: %w", err)
    }
    sharedSecret, err := serverPrivate.ECDH(clientPub)
    if err != nil {
        return nil, fmt.Errorf("ecdh: %w", err)
    }

    return newSecureConn(conn, sharedSecret)
}

// clientHandshake 客户端握手
func clientHandshake(conn net.Conn, trustedServerPub ed25519.PublicKey) (*SecureConn, error) {
    curve := ecdh.X25519()

    // 1. 生成临时 ECDH 密钥对
    clientPrivate, _ := curve.GenerateKey(rand.Reader)
    clientPublic := clientPrivate.PublicKey().Bytes()

    // 2. 发送客户端公钥
    conn.Write(clientPublic)

    // 3. 接收服务端公钥(32 字节)
    serverPubBytes := make([]byte, 32)
    if _, err := io.ReadFull(conn, serverPubBytes); err != nil {
        return nil, fmt.Errorf("read server public key: %w", err)
    }

    // 4. 接收并验证服务端签名
    serverSignPub := make([]byte, ed25519.PublicKeySize)
    if _, err := io.ReadFull(conn, serverSignPub); err != nil {
        return nil, fmt.Errorf("read sign public key: %w", err)
    }
    signature := make([]byte, ed25519.SignatureSize)
    if _, err := io.ReadFull(conn, signature); err != nil {
        return nil, fmt.Errorf("read signature: %w", err)
    }

    // 验证身份:签名公钥必须是我们信任的
    if string(serverSignPub) != string(trustedServerPub) {
        return nil, fmt.Errorf("untrusted server")
    }

    signData := append(clientPublic, serverPubBytes...)
    if !ed25519.Verify(serverSignPub, signData, signature) {
        return nil, fmt.Errorf("invalid server signature")
    }
    fmt.Println("  ✅ 服务端身份验证通过")

    // 5. 计算共享密钥
    serverPub, err := curve.NewPublicKey(serverPubBytes)
    if err != nil {
        return nil, err
    }
    sharedSecret, err := clientPrivate.ECDH(serverPub)
    if err != nil {
        return nil, err
    }

    return newSecureConn(conn, sharedSecret)
}

// ==================== 服务端和客户端 ====================

func startServer(serverSignKey ed25519.PrivateKey) {
    listener, err := net.Listen("tcp", ":7443")
    if err != nil {
        fmt.Println("监听失败:", err)
        return
    }
    defer listener.Close()
    fmt.Println("[Server] 启动,监听 :7443")

    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }

        go func(c net.Conn) {
            fmt.Println("[Server] 新连接,开始握手...")

            // TLS 握手
            secure, err := serverHandshake(c, serverSignKey)
            if err != nil {
                fmt.Println("[Server] 握手失败:", err)
                c.Close()
                return
            }
            defer secure.Close()
            fmt.Println("[Server] 握手成功,加密通道已建立")

            // 加密通信
            for {
                data, err := secure.Recv()
                if err != nil {
                    fmt.Println("[Server] 接收错误:", err)
                    return
                }
                fmt.Printf("[Server] 收到加密消息: %s\n", data)

                // 回复
                reply := fmt.Sprintf("服务端确认: %s", data)
                if err := secure.Send([]byte(reply)); err != nil {
                    return
                }
            }
        }(conn)
    }
}

func startClient(trustedPub ed25519.PublicKey) {
    conn, err := net.Dial("tcp", "localhost:7443")
    if err != nil {
        fmt.Println("[Client] 连接失败:", err)
        return
    }

    fmt.Println("[Client] 已连接,开始握手...")

    // TLS 握手
    secure, err := clientHandshake(conn, trustedPub)
    if err != nil {
        fmt.Println("[Client] 握手失败:", err)
        return
    }
    defer secure.Close()
    fmt.Println("[Client] 握手成功,加密通道已建立")

    // 加密通信
    messages := []string{
        "Hello, Secure World!",
        "这条消息是加密传输的",
        "即使被截获也无法读取",
    }

    for _, msg := range messages {
        fmt.Printf("[Client] 发送: %s\n", msg)
        if err := secure.Send([]byte(msg)); err != nil {
            fmt.Println("[Client] 发送失败:", err)
            return
        }

        reply, err := secure.Recv()
        if err != nil {
            fmt.Println("[Client] 接收失败:", err)
            return
        }
        fmt.Printf("[Client] 收到回复: %s\n", reply)
    }
}

func main() {
    // 服务端长期签名密钥(实际中由 CA 签发证书)
    serverPub, serverPriv, _ := ed25519.GenerateKey(rand.Reader)

    // 客户端预置信任的服务端公钥(实际中是信任 CA 根证书)
    trustedPub := serverPub

    fmt.Println("=== 手动 TLS 演示 ===")
    fmt.Printf("服务端签名公钥: %x\n\n", serverPub)

    // 启动服务端
    go startServer(serverPriv)
    time.Sleep(200 * time.Millisecond)

    // 启动客户端
    startClient(trustedPub)
}

9.3 Go 标准库的 TLS(生产用法)

go
package main

import (
    "crypto/tls"
    "fmt"
    "net"
)

// 生产环境直接使用 crypto/tls,不要自己实现!

func tlsServer() {
    // 加载证书(由 CA 签发或 Let's Encrypt 免费获取)
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        fmt.Println("加载证书失败:", err)
        return
    }

    config := &tls.Config{
        Certificates: []tls.Certificate{cert},
        MinVersion:   tls.VersionTLS13, // 强制 TLS 1.3
    }

    listener, err := tls.Listen("tcp", ":443", config)
    if err != nil {
        fmt.Println("监听失败:", err)
        return
    }
    defer listener.Close()

    for {
        conn, _ := listener.Accept()
        go func(c net.Conn) {
            defer c.Close()
            buf := make([]byte, 1024)
            n, _ := c.Read(buf)
            fmt.Println("收到:", string(buf[:n]))
            c.Write([]byte("encrypted response"))
        }(conn)
    }
}

func tlsClient() {
    config := &tls.Config{
        MinVersion: tls.VersionTLS13,
        // InsecureSkipVerify: true, // ❌ 不要在生产中使用
    }

    conn, err := tls.Dial("tcp", "localhost:443", config)
    if err != nil {
        fmt.Println("连接失败:", err)
        return
    }
    defer conn.Close()

    // 查看连接信息
    state := conn.ConnectionState()
    fmt.Printf("TLS 版本: %x\n", state.Version)
    fmt.Printf("密码套件: %x\n", state.CipherSuite)

    conn.Write([]byte("hello secure"))
    buf := make([]byte, 1024)
    n, _ := conn.Read(buf)
    fmt.Println("响应:", string(buf[:n]))
}

附录:加密算法速查

text
场景                          推荐算法                   Go 包
──────────────────────────────────────────────────────────────
对称加密                      AES-256-GCM               crypto/aes + cipher
文件加密                      AES-256-CTR + Argon2       crypto/aes + x/crypto/argon2
密码存储                      bcrypt / Argon2id          x/crypto/bcrypt
数据完整性                    SHA-256                    crypto/sha256
文件校验                      SHA-256                    crypto/sha256
数字签名                      Ed25519                    crypto/ed25519
RSA 加密                     RSA-OAEP-SHA256            crypto/rsa
密钥交换                      X25519 (ECDH)              crypto/ecdh
TLS 通信                     crypto/tls (TLS 1.3)       crypto/tls
随机数生成                    crypto/rand                crypto/rand
HMAC 消息认证                HMAC-SHA256                crypto/hmac

❌ 不要用:
  - MD5(已破解,仅用于非安全场景的校验)
  - SHA-1(已破解)
  - DES / 3DES(密钥太短)
  - RC4(已破解)
  - ECB 模式(泄露模式信息)
  - 自研加密算法