Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go 语言标准库 —— crypto/cipher 包(分组密码模式)


🔹 概述

crypto/cipher 包实现了标准的分组密码模式(Block Cipher Modes),这些模式可以包装底层分组密码实现(如 AES)。

主要功能:

  • 提供标准密码模式的实现
  • 包装底层分组密码(如 AES、DES 等)
  • 支持多种工作模式(CBC、CTR、CFB、OFB、GCM)
  • 提供认证加密(AEAD)支持

重要说明:

  • ⚠️ cipher 包本身不提供加密算法,只提供工作模式
  • ✅ 需要配合具体的分组密码使用(如 crypto/aes
  • ✅ 遵循 NIST 标准(Special Publication 800-38A)
  • 🔒 推荐使用认证加密模式(GCM)

核心接口:

  • Block - 分组密码接口
  • BlockMode - 分组密码模式接口(CBC 等)
  • Stream - 流密码接口(CTR、CFB、OFB)
  • AEAD - 认证加密接口(GCM)

🔹 核心接口

Block 接口

type Block interface {
    BlockSize() int
    Encrypt(dst, src []byte)
    Decrypt(dst, src []byte)
}
  • 说明:

    • 表示使用给定密钥的分组密码实现
    • 提供加密或解密单个块的能力
    • 模式实现将该能力扩展到块流
  • 方法:

    • BlockSize() int - 返回分组大小(字节数)
    • Encrypt(dst, src []byte) - 加密单个块
    • Decrypt(dst, src []byte) - 解密单个块
  • 注意事项:

    • ⚠️ dstsrc 可以重叠(支持原地加密)
    • ⚠️ src 长度必须等于 BlockSize()
    • ⚠️ 不处理填充,需要手动处理
  • 示例(AES Cipher):

    package main
    
    import (
    	"crypto/aes"
    	"fmt"
    )
    
    func main() {
    	// 创建 AES cipher(实现 Block 接口)
    	key := []byte("12345678901234567890123456789012")
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		fmt.Println("错误:", err)
    		return
    	}
    	
    	// 获取分组大小
    	fmt.Printf("分组大小:%d 字节\n", block.BlockSize())
    	
    	// 准备数据(必须等于分组大小)
    	plaintext := []byte("1234567890123456") // 16 字节
    	ciphertext := make([]byte, 16)
    	
    	// 加密单个块
    	block.Encrypt(ciphertext, plaintext)
    	fmt.Printf("密文:%x\n", ciphertext)
    	
    	// 解密单个块
    	decrypted := make([]byte, 16)
    	block.Decrypt(decrypted, ciphertext)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

BlockMode 接口

type BlockMode interface {
    BlockSize() int
    CryptBlocks(dst, src []byte)
}
  • 说明:

    • 表示运行在基于块的模式(CBC、ECB 等)的分组密码
    • 用于处理多块数据
  • 方法:

    • BlockSize() int - 返回分组大小
    • CryptBlocks(dst, src []byte) - 加密或解密多个块
  • 注意事项:

    • ⚠️ src 长度必须是 BlockSize() 的整数倍
    • ⚠️ 不处理填充,需要手动 PKCS7 填充
    • ⚠️ dstsrc 可以重叠
  • 实现函数:

    • NewCBCEncrypter(block Block, iv []byte) BlockMode - CBC 加密
    • NewCBCDecrypter(block Block, iv []byte) BlockMode - CBC 解密

Stream 接口

type Stream interface {
    XORKeyStream(dst, src []byte)
}
  • 说明:

    • 表示流密码
    • 将分组密码转换为流密码
    • 不需要填充
  • 方法:

    • XORKeyStream(dst, src []byte) - 使用密钥流异或数据
  • 注意事项:

    • dstsrc 可以重叠
    • ✅ 支持任意长度的数据
    • ✅ 加密和解密使用相同的操作
    • ⚠️ 不要重复使用相同的 nonce/IV
  • 实现函数:

    • NewCTR(block Block, iv []byte) Stream - CTR 模式(✅ 推荐)
    • NewCFBEncrypter(block Block, iv []byte) Stream - CFB 加密(⚠️ 已弃用)
    • NewCFBDecrypter(block Block, iv []byte) Stream - CFB 解密(⚠️ 已弃用)
    • NewOFB(block Block, iv []byte) Stream - OFB 模式(⚠️ 已弃用)

AEAD 接口(认证加密)

type AEAD interface {
    NonceSize() int
    Overhead() int
    Seal(dst, nonce, plaintext, additionalData []byte) []byte
    Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
}
  • 说明:

    • 提供带关联数据的认证加密(Authenticated Encryption with Associated Data)
    • 同时保证机密性和完整性
    • 推荐使用
  • 方法:

    • NonceSize() int - 返回 nonce 大小
    • Overhead() int - 返回加密开销(认证标签大小)
    • Seal(...) - 加密并添加认证标签
    • Open(...) - 解密并验证认证标签
  • 注意事项:

    • ✅ 自动处理认证标签
    • ✅ 验证数据完整性
    • ⚠️ 不要重复使用 nonce
    • ⚠️ additionalData 会被认证但不会加密
  • 实现函数:

    • NewGCM(block Block) (AEAD, error) - GCM 模式(✅ 推荐)
    • NewGCMWithNonceSize(block Block, size int) (AEAD, error) - 自定义 nonce 大小
    • NewGCMWithTagSize(block Block, tagSize int) (AEAD, error) - 自定义标签大小
    • NewGCMWithRandomNonce(block Block) (AEAD, error) - 随机 nonce(Go 1.24+)

🔹 工作模式详解

CBC 模式(BlockMode)

cipher.NewCBCEncrypter(block cipher.Block, iv []byte) BlockMode

  • 说明:

    • Cipher Block Chaining(密码块链接)
    • 每个明文块与前一个密文块异或后再加密
    • 需要初始化向量(IV)
  • 特点:

    • ⚠️ 仅加密,无认证
    • ⚠️ 需要 PKCS7 填充
    • ⚠️ 串行加密,并行解密
    • ⚠️ 已不推荐用于新系统
  • 示例(完整):

    package main
    
    import (
    	"bytes"
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"encoding/hex"
    	"fmt"
    	"io"
    )
    
    // PKCS7 填充
    func pkcs7Pad(data []byte, blockSize int) []byte {
    	padding := blockSize - len(data)%blockSize
    	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    	return append(data, padtext...)
    }
    
    // PKCS7 去填充
    func pkcs7Unpad(data []byte) ([]byte, error) {
    	if len(data) == 0 {
    		return nil, fmt.Errorf("数据为空")
    	}
    	padding := int(data[len(data)-1])
    	if padding > len(data) || padding == 0 {
    		return nil, fmt.Errorf("无效的填充")
    	}
    	for i := 0; i < padding; i++ {
    		if data[len(data)-1-i] != byte(padding) {
    			return nil, fmt.Errorf("无效的填充")
    		}
    	}
    	return data[:len(data)-padding], nil
    }
    
    // CBC 加密
    func encryptCBC(plaintext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	// 填充
    	plaintext = pkcs7Pad(plaintext, block.BlockSize())
    	
    	// 生成随机 IV
    	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    	iv := ciphertext[:aes.BlockSize]
    	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    		return nil, err
    	}
    	
    	// CBC 加密
    	mode := cipher.NewCBCEncrypter(block, iv)
    	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
    	
    	return ciphertext, nil
    }
    
    // CBC 解密
    func decryptCBC(ciphertext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	if len(ciphertext) < aes.BlockSize {
    		return nil, fmt.Errorf("密文太短")
    	}
    	
    	// 分离 IV 和密文
    	iv := ciphertext[:aes.BlockSize]
    	ciphertext = ciphertext[aes.BlockSize:]
    	
    	// CBC 解密
    	mode := cipher.NewCBCDecrypter(block, iv)
    	mode.CryptBlocks(ciphertext, ciphertext)
    	
    	// 去填充
    	plaintext, err := pkcs7Unpad(ciphertext)
    	return plaintext, err
    }
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	plaintext := []byte("Hello, CBC Mode!")
    	
    	ciphertext, _ := encryptCBC(plaintext, key)
    	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
    	
    	decrypted, _ := decryptCBC(ciphertext, key)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

CTR 模式(Stream)

cipher.NewCTR(block cipher.Block, iv []byte) Stream

  • 说明:

    • Counter Mode(计数器模式)
    • 将分组密码转换为流密码
    • 推荐用于流式加密
  • 特点:

    • ⚠️ 仅加密,无认证
    • ✅ 不需要填充
    • ✅ 支持并行加密/解密
    • ✅ 加密和解密操作相同
  • 示例(完整):

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"encoding/hex"
    	"fmt"
    	"io"
    )
    
    // CTR 加密/解密(相同操作)
    func encryptCTR(plaintext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	// 生成随机 nonce
    	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    	nonce := ciphertext[:aes.BlockSize]
    	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
    		return nil, err
    	}
    	
    	// CTR 模式
    	stream := cipher.NewCTR(block, nonce)
    	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
    	
    	return ciphertext, nil
    }
    
    // CTR 解密
    func decryptCTR(ciphertext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	if len(ciphertext) < aes.BlockSize {
    		return nil, fmt.Errorf("密文太短")
    	}
    	
    	// 分离 nonce 和密文
    	nonce := ciphertext[:aes.BlockSize]
    	ciphertext = ciphertext[aes.BlockSize:]
    	
    	// CTR 模式(解密相同)
    	stream := cipher.NewCTR(block, nonce)
    	plaintext := make([]byte, len(ciphertext))
    	stream.XORKeyStream(plaintext, ciphertext)
    	
    	return plaintext, nil
    }
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	plaintext := []byte("Hello, CTR Mode!")
    	
    	ciphertext, _ := encryptCTR(plaintext, key)
    	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
    	
    	decrypted, _ := decryptCTR(ciphertext, key)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

GCM 模式(AEAD,推荐)

cipher.NewGCM(block cipher.Block) (AEAD, error)

  • 说明:

    • Galois/Counter Mode
    • 提供认证加密(AEAD)
    • 强烈推荐使用
  • 特点:

    • ✅ 加密 + 认证
    • ✅ 高性能
    • ✅ 不需要填充
    • ✅ 自动验证完整性
    • ⚠️ 需要唯一的 nonce
  • 示例(完整):

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"encoding/hex"
    	"fmt"
    	"io"
    )
    
    // GCM 加密
    func encryptGCM(plaintext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	// 创建 GCM
    	gcm, err := cipher.NewGCM(block)
    	if err != nil {
    		return nil, err
    	}
    	
    	// 生成随机 nonce
    	nonce := make([]byte, gcm.NonceSize())
    	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
    		return nil, err
    	}
    	
    	// 加密(包含 nonce 和认证标签)
    	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
    	return ciphertext, nil
    }
    
    // GCM 解密
    func decryptGCM(ciphertext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	gcm, err := cipher.NewGCM(block)
    	if err != nil {
    		return nil, err
    	}
    	
    	nonceSize := gcm.NonceSize()
    	if len(ciphertext) < nonceSize {
    		return nil, fmt.Errorf("密文太短")
    	}
    	
    	// 分离 nonce 和密文
    	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    	
    	// 解密并验证
    	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
    	return plaintext, err
    }
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	plaintext := []byte("Hello, GCM Mode!")
    	
    	ciphertext, _ := encryptGCM(plaintext, key)
    	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
    	
    	decrypted, _ := decryptGCM(ciphertext, key)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

CFB 模式(已弃用)

cipher.NewCFBEncrypter(block cipher.Block, iv []byte) Stream

  • 说明:

    • Cipher Feedback Mode
    • ⚠️ 已弃用,不推荐使用
  • 弃用原因:

    • ❌ 未认证,易受主动攻击
    • ❌ 实现未优化
    • ❌ 未通过 FIPS 140-3 认证
    • ✅ 建议使用 CTR 或 GCM 替代
  • 示例(仅供参考):

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"encoding/hex"
    	"fmt"
    	"io"
    )
    
    // CFB 加密(已弃用)
    func encryptCFB(plaintext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    	iv := ciphertext[:aes.BlockSize]
    	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    		return nil, err
    	}
    	
    	stream := cipher.NewCFBEncrypter(block, iv)
    	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
    	
    	return ciphertext, nil
    }
    
    // CFB 解密(已弃用)
    func decryptCFB(ciphertext []byte, key []byte) ([]byte, error) {
    	block, err := aes.NewCipher(key)
    	if err != nil {
    		return nil, err
    	}
    	
    	if len(ciphertext) < aes.BlockSize {
    		return nil, fmt.Errorf("密文太短")
    	}
    	
    	iv := ciphertext[:aes.BlockSize]
    	ciphertext = ciphertext[aes.BlockSize:]
    	
    	stream := cipher.NewCFBDecrypter(block, iv)
    	plaintext := make([]byte, len(ciphertext))
    	stream.XORKeyStream(plaintext, ciphertext)
    	
    	return plaintext, nil
    }
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	plaintext := []byte("Hello, CFB Mode!")
    	
    	ciphertext, _ := encryptCFB(plaintext, key)
    	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
    	
    	decrypted, _ := decryptCFB(ciphertext, key)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

OFB 模式(已弃用)

cipher.NewOFB(block cipher.Block, iv []byte) Stream

  • 说明:
    • Output Feedback Mode
    • ⚠️ 已弃用,不推荐使用
  • 弃用原因:
    • ❌ 未认证,易受主动攻击
    • ❌ 实现未优化
    • ❌ 未通过 FIPS 140-3 认证
    • ✅ 建议使用 CTR 或 GCM 替代

🔹 GCM 高级用法

自定义 Nonce 大小

cipher.NewGCMWithNonceSize(block cipher.Block, size int) (AEAD, error)

  • 说明:

    • 创建使用自定义 nonce 大小的 GCM
    • ⚠️ 仅用于兼容现有系统
    • ❌ 不推荐用于新系统
  • 示例:

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"fmt"
    )
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	block, _ := aes.NewCipher(key)
    	
    	// 标准 GCM(12 字节 nonce)
    	gcm1, _ := cipher.NewGCM(block)
    	fmt.Printf("标准 GCM nonce 大小:%d\n", gcm1.NonceSize())
    	
    	// 自定义 nonce 大小(16 字节)
    	gcm2, _ := cipher.NewGCMWithNonceSize(block, 16)
    	fmt.Printf("自定义 GCM nonce 大小:%d\n", gcm2.NonceSize())
    }
    

自定义标签大小

cipher.NewGCMWithTagSize(block cipher.Block, tagSize int) (AEAD, error)

  • 说明:

    • 创建使用自定义认证标签大小的 GCM
    • 标签大小:12-16 字节
    • ⚠️ 仅用于兼容现有系统
  • 示例:

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"fmt"
    )
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	block, _ := aes.NewCipher(key)
    	
    	// 标准 GCM(16 字节标签)
    	gcm1, _ := cipher.NewGCM(block)
    	fmt.Printf("标准 GCM 开销:%d\n", gcm1.Overhead())
    	
    	// 自定义标签大小(12 字节)
    	gcm2, _ := cipher.NewGCMWithTagSize(block, 12)
    	fmt.Printf("自定义 GCM 开销:%d\n", gcm2.Overhead())
    }
    

随机 Nonce(Go 1.24+)

cipher.NewGCMWithRandomNonce(block cipher.Block) (AEAD, error)

  • 说明:

    • Go 1.24+ 新增功能
    • 自动生成随机 96 位 nonce
    • nonce 会前置到密文中
    • ✅ 简化使用,减少 nonce 重用风险
  • 示例:

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	block, _ := aes.NewCipher(key)
    	
    	// 创建带随机 nonce 的 GCM
    	gcm, _ := cipher.NewGCMWithRandomNonce(block)
    	
    	// NonceSize 为 0(nonce 由内部生成)
    	fmt.Printf("NonceSize: %d\n", gcm.NonceSize())
    	fmt.Printf("Overhead: %d (12 字节 nonce + 16 字节标签)\n", gcm.Overhead())
    	
    	plaintext := []byte("Hello, Random Nonce!")
    	
    	// 加密(不需要提供 nonce)
    	ciphertext := gcm.Seal(nil, nil, plaintext, nil)
    	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
    	
    	// 解密(自动提取 nonce)
    	decrypted, _ := gcm.Open(nil, nil, ciphertext, nil)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

🔹 Stream 包装器

StreamReader

type StreamReader struct {
    S Stream
    R io.Reader
}
  • 说明:

    • 将 Stream 包装成 io.Reader
    • 自动对读取的数据进行解密
  • 使用场景:

    • 流式解密文件
    • 网络流解密
  • 示例:

    package main
    
    import (
    	"bytes"
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"fmt"
    	"io"
    )
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	block, _ := aes.NewCipher(key)
    	
    	// 生成 nonce
    	nonce := make([]byte, aes.BlockSize)
    	io.ReadFull(rand.Reader, nonce)
    	
    	// 创建 CTR 流
    	stream := cipher.NewCTR(block, nonce)
    	
    	// 准备数据
    	plaintext := []byte("This is secret data that will be decrypted on the fly.")
    	ciphertext := make([]byte, len(plaintext))
    	stream.XORKeyStream(ciphertext, plaintext)
    	
    	// 创建 StreamReader
    	reader := &cipher.StreamReader{
    		S: cipher.NewCTR(block, nonce),
    		R: bytes.NewReader(ciphertext),
    	}
    	
    	// 流式读取并解密
    	decrypted, _ := io.ReadAll(reader)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

StreamWriter

type StreamWriter struct {
    S Stream
    W io.Writer
}
  • 说明:

    • 将 Stream 包装成 io.Writer
    • 自动对写入的数据进行加密
  • 使用场景:

    • 流式加密文件
    • 网络流加密
  • 示例:

    package main
    
    import (
    	"bytes"
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/rand"
    	"fmt"
    	"io"
    )
    
    func main() {
    	key := []byte("12345678901234567890123456789012")
    	block, _ := aes.NewCipher(key)
    	
    	// 生成 nonce
    	nonce := make([]byte, aes.BlockSize)
    	io.ReadFull(rand.Reader, nonce)
    	
    	// 创建缓冲区
    	var buf bytes.Buffer
    	buf.Write(nonce) // 先写入 nonce
    	
    	// 创建 StreamWriter
    	writer := &cipher.StreamWriter{
    		S: cipher.NewCTR(block, nonce),
    		W: &buf,
    	}
    	
    	// 流式写入并加密
    	plaintext := []byte("This is secret data that will be encrypted on the fly.")
    	writer.Write(plaintext)
    	writer.Close()
    	
    	fmt.Printf("密文:%x\n", buf.Bytes())
    	
    	// 解密验证
    	ciphertext := buf.Bytes()[aes.BlockSize:] // 跳过 nonce
    	decrypted := make([]byte, len(ciphertext))
    	cipher.NewCTR(block, nonce).XORKeyStream(decrypted, ciphertext)
    	fmt.Printf("明文:%s\n", string(decrypted))
    }
    

🔹 使用场景

1. 文件加密(GCM 模式)

package main

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

func encryptFile(inputPath, outputPath string, key []byte) error {
	// 读取明文文件
	plaintext, err := os.ReadFile(inputPath)
	if err != nil {
		return err
	}

	// 创建 cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	// 生成 nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return err
	}

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

	// 写入密文文件
	return os.WriteFile(outputPath, ciphertext, 0600)
}

func decryptFile(inputPath, outputPath string, key []byte) error {
	// 读取密文文件
	ciphertext, err := os.ReadFile(inputPath)
	if err != nil {
		return err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return fmt.Errorf("密文太短")
	}

	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

	// 解密并验证
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return err
	}

	return os.WriteFile(outputPath, plaintext, 0600)
}

func main() {
	key := []byte("12345678901234567890123456789012")

	// 加密文件
	err := encryptFile("secret.txt", "secret.txt.enc", key)
	if err != nil {
		fmt.Println("加密失败:", err)
		return
	}
	fmt.Println("加密成功")

	// 解密文件
	err = decryptFile("secret.txt.enc", "restored.txt", key)
	if err != nil {
		fmt.Println("解密失败:", err)
		return
	}
	fmt.Println("解密成功")
}

2. 流式加密大文件

package main

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

func encryptLargeFile(inputPath, outputPath string, key []byte) error {
	// 打开文件
	inputFile, err := os.Open(inputPath)
	if err != nil {
		return err
	}
	defer inputFile.Close()

	outputFile, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer outputFile.Close()

	// 创建 cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	// 生成 nonce
	nonce := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return err
	}

	// 写入 nonce
	if _, err := outputFile.Write(nonce); err != nil {
		return err
	}

	// 创建 CTR 流
	stream := cipher.NewCTR(block, nonce)

	// 创建 StreamWriter
	writer := &cipher.StreamWriter{
		S: stream,
		W: outputFile,
	}
	defer writer.Close()

	// 流式复制(加密)
	buffer := make([]byte, 32*1024) // 32KB 缓冲区
	_, err = io.CopyBuffer(writer, inputFile, buffer)
	return err
}

func decryptLargeFile(inputPath, outputPath string, key []byte) error {
	inputFile, err := os.Open(inputPath)
	if err != nil {
		return err
	}
	defer inputFile.Close()

	outputFile, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer outputFile.Close()

	// 读取 nonce
	nonce := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(inputFile, nonce); err != nil {
		return err
	}

	// 创建 cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	// 创建 CTR 流
	stream := cipher.NewCTR(block, nonce)

	// 创建 StreamReader
	reader := &cipher.StreamReader{
		S: stream,
		R: inputFile,
	}

	// 流式复制(解密)
	buffer := make([]byte, 32*1024)
	_, err = io.CopyBuffer(outputFile, reader, buffer)
	return err
}

func main() {
	key := []byte("12345678901234567890123456789012")

	// 加密大文件
	err := encryptLargeFile("largefile.dat", "largefile.dat.enc", key)
	if err != nil {
		fmt.Println("加密失败:", err)
		return
	}
	fmt.Println("大文件加密成功")

	// 解密大文件
	err = decryptLargeFile("largefile.dat.enc", "largefile.dat.dec", key)
	if err != nil {
		fmt.Println("解密失败:", err)
		return
	}
	fmt.Println("大文件解密成功")
}

3. 带关联数据的认证加密

package main

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

func main() {
	key := []byte("12345678901234567890123456789012")
	block, _ := aes.NewCipher(key)
	gcm, _ := cipher.NewGCM(block)

	// 生成 nonce
	nonce := make([]byte, gcm.NonceSize())
	io.ReadFull(rand.Reader, nonce)

	plaintext := []byte("Secret message")
	
	// 关联数据(会被认证但不会加密)
	additionalData := []byte("header-info:v1.0|user:alice|timestamp:1234567890")

	// 加密(包含关联数据)
	ciphertext := gcm.Seal(nonce, nonce, plaintext, additionalData)
	fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))

	// 分离 nonce 和密文
	nonceSize := gcm.NonceSize()
	recvNonce, recvCiphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

	// 解密并验证(需要相同的关联数据)
	plaintext2, err := gcm.Open(nil, recvNonce, recvCiphertext, additionalData)
	if err != nil {
		fmt.Println("验证失败:", err)
		return
	}
	fmt.Printf("明文:%s\n", string(plaintext2))

	// 如果关联数据被篡改,会验证失败
	wrongData := []byte("header-info:v1.0|user:bob|timestamp:1234567890")
	_, err = gcm.Open(nil, recvNonce, recvCiphertext, wrongData)
	if err != nil {
		fmt.Println("关联数据验证失败(预期):", err)
	}
}

🔹 注意事项和最佳实践

1. 模式选择

  • 优先使用 GCM 模式
    • 提供认证加密
    • 高性能
    • 不需要填充
  • 流式加密使用 CTR
    • 不需要填充
    • 可并行处理
  • ⚠️ 避免使用 CFB/OFB
    • 已弃用
    • 未认证
    • 未优化
  • 不要使用 ECB
    • 不安全
    • 暴露数据模式
// 推荐
gcm, _ := cipher.NewGCM(block)
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

// 流式加密
stream := cipher.NewCTR(block, nonce)
stream.XORKeyStream(ciphertext, plaintext)

// 不推荐(已弃用)
stream := cipher.NewCFBEncrypter(block, iv) // 已弃用

// 绝对禁止(不安全)
// ECB 模式会暴露数据模式

2. Nonce/IV 管理

  • ✅ 使用 crypto/rand 生成随机 nonce/IV
  • ❌ 不要重复使用 nonce(GCM)
  • ❌ 不要重复使用 IV(CBC/CTR)
  • ⚠️ GCM nonce 重用是灾难性的
// 正确
nonce := make([]byte, gcm.NonceSize())
io.ReadFull(rand.Reader, nonce)

// 错误 - 固定 nonce
nonce := make([]byte, 12) // 全 0

// 错误 - 重复使用 nonce
// 第一次加密
ciphertext1 := gcm.Seal(nonce, nonce, plaintext1, nil)
// 第二次加密(使用相同 nonce)- 危险!
ciphertext2 := gcm.Seal(nonce, nonce, plaintext2, nil)

3. 填充处理

  • ⚠️ CBC 模式需要手动 PKCS7 填充
  • ✅ CTR/GCM 不需要填充
  • ⚠️ 验证填充的正确性
// CBC 需要填充
plaintext = pkcs7Pad(plaintext, block.BlockSize())

// CTR/GCM 不需要填充
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

4. 认证验证

  • ✅ 始终验证 GCM 的认证标签
  • ✅ 检查 Open() 返回的错误
  • ⚠️ CBC/CTR 需要额外 HMAC 验证
// GCM 自动认证
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
	// 认证失败,数据可能被篡改
	return nil, err
}

// CBC/CTR 需要额外 HMAC
hmac := computeHMAC(ciphertext, key2)
// 验证 hmac

5. 错误处理

  • ✅ 检查所有错误
  • ✅ 不要泄露敏感信息
  • ✅ 清理临时数据
block, err := aes.NewCipher(key)
if err != nil {
	return nil, err // 不要泄露密钥信息
}

gcm, err := cipher.NewGCM(block)
if err != nil {
	return nil, err
}

plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
	// 认证失败,不要返回部分数据
	return nil, fmt.Errorf("解密失败")
}

6. 性能优化

  • ✅ 使用硬件加速(AES-NI)
  • ✅ 重用 Block 实例
  • ✅ 使用合适的缓冲区大小
  • ✅ 流式处理大文件
// 重用 Block 实例
block, _ := aes.NewCipher(key)
gcm, _ := cipher.NewGCM(block)

// 多次使用
for _, data := range dataList {
	ciphertext := gcm.Seal(nil, nonce, data, nil)
}

// 流式处理大文件
buffer := make([]byte, 32*1024) // 32KB
io.CopyBuffer(writer, reader, buffer)

🔥 总结

核心接口

接口说明实现
Block分组密码接口AES、DES 等
BlockMode分组密码模式CBC、ECB
Stream流密码接口CTR、CFB、OFB
AEAD认证加密接口GCM

工作模式对比

模式类型认证填充并行加密并行解密推荐度
GCMAEAD✅ 强烈推荐
CBCBlockMode⚠️ 常用
CTRStream✅ 推荐
CFBStream❌ 已弃用
OFBStream❌ 已弃用
ECBBlockMode❌ 不安全

GCM 变体

函数说明使用场景
NewGCM()标准 GCM(12 字节 nonce)✅ 推荐
NewGCMWithNonceSize()自定义 nonce 大小⚠️ 兼容性
NewGCMWithTagSize()自定义标签大小⚠️ 兼容性
NewGCMWithRandomNonce()随机 nonce(Go 1.24+)✅ 简化使用

主要特点

  • 模式包装 👉 包装底层分组密码实现
  • 标准实现 👉 遵循 NIST 标准
  • 多种模式 👉 CBC、CTR、GCM 等
  • 认证加密 👉 GCM 提供完整性保证
  • 流式处理 👉 StreamReader/Writer 支持

使用场景

  • 文件加密 👉 GCM 模式
  • 大文件流式加密 👉 CTR + StreamWriter
  • 网络传输 👉 GCM/CTR 模式
  • 数据库加密 👉 GCM 模式
  • 认证加密 👉 GCM + Additional Data

最佳实践

  • ✅ 优先使用 GCM 模式
  • ✅ 使用 crypto/rand 生成 nonce/IV
  • ✅ 不要重复使用 nonce
  • ✅ 验证 GCM 认证标签
  • ✅ 重用 Block 实例
  • ✅ 流式处理大文件
  • ⚠️ CFB/OFB 已弃用,使用 CTR 替代
  • ❌ 避免使用 ECB 模式

安全建议

  • 🔒 使用 AES-256(32 字节密钥)
  • 🔒 实施 nonce 重用检测
  • 🔒 记录加密操作日志
  • 🔒 进行安全审计
  • 🔒 定期轮换密钥

crypto/cipher 包提供了标准的分组密码模式实现,请始终使用 GCM 模式进行认证加密!