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)- 解密单个块
-
注意事项:
- ⚠️
dst和src可以重叠(支持原地加密) - ⚠️
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 填充
- ⚠️
dst和src可以重叠
- ⚠️
-
实现函数:
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)- 使用密钥流异或数据
-
注意事项:
- ✅
dst和src可以重叠 - ✅ 支持任意长度的数据
- ✅ 加密和解密使用相同的操作
- ⚠️ 不要重复使用相同的 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 |
工作模式对比
| 模式 | 类型 | 认证 | 填充 | 并行加密 | 并行解密 | 推荐度 |
|---|---|---|---|---|---|---|
| GCM | AEAD | ✅ | ❌ | ✅ | ✅ | ✅ 强烈推荐 |
| CBC | BlockMode | ❌ | ✅ | ❌ | ✅ | ⚠️ 常用 |
| CTR | Stream | ❌ | ❌ | ✅ | ✅ | ✅ 推荐 |
| CFB | Stream | ❌ | ❌ | ❌ | ❌ | ❌ 已弃用 |
| OFB | Stream | ❌ | ❌ | ❌ | ✅ | ❌ 已弃用 |
| ECB | BlockMode | ❌ | ✅ | ✅ | ✅ | ❌ 不安全 |
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 模式进行认证加密!