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 速度太快,攻击者可以暴力穷举。存储密码必须用专门的密码哈希函数:bcrypt、argon2、scrypt,它们故意设计得很慢。
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 模式(泄露模式信息)
- 自研加密算法