AES-GCM 加密解密笔记

概述

这份笔记展示了如何使用 Go 语言实现 AES-GCM 模式的加密和解密。AES-GCM 是一种高效且安全的认证加密模式,提供了数据的机密性和完整性保护。

代码实现

以下是完整的 AES-GCM 加密解密实现:

package main

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

// AESGCMEncrypt 使用 AES-GCM 模式加密明文
// 参数:
//   - key: AES 密钥 (16/24/32 字节对应 AES-128/192/256)
//   - plaintext: 需要加密的明文字符串
// 返回:
//   - 加密后的 Base64 编码字符串
//   - 错误信息
func AESGCMEncrypt(key []byte, plaintext string) (string, error) {
    // 创建 AES 密码块
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", fmt.Errorf("无法创建 AES 密钥块: %v", err)
    }

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

    // 生成随机 nonce (Number used ONCE)
    nonce := make([]byte, gcm.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return "", fmt.Errorf("无法生成随机 nonce: %v", err)
    }

    // 加密数据 (nonce 会被添加到密文前面)
    ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
    
    // Base64 编码密文
    return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// AESGCMDecrypt 使用 AES-GCM 模式解密密文
// 参数:
//   - key: 与加密时相同的 AES 密钥
//   - ciphertextBase64: Base64 编码的密文
// 返回:
//   - 解密后的明文字符串
//   - 错误信息
func AESGCMDecrypt(key []byte, ciphertextBase64 string) (string, error) {
    // Base64 解码密文
    ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
    if err != nil {
        return "", fmt.Errorf("无法解码密文: %v", err)
    }

    // 创建 AES 密码块
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", fmt.Errorf("无法创建 AES 密钥块: %v", err)
    }

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

    // 获取 nonce 大小
    nonceSize := gcm.NonceSize()
    if len(ciphertext) < nonceSize {
        return "", fmt.Errorf("密文长度不足以包含 nonce")
    }

    // 从密文中提取 nonce 和实际密文
    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    
    // 解密数据
    plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return "", fmt.Errorf("解密失败: %v", err)
    }

    return string(plaintext), nil
}

// GenerateKey 生成指定长度的随机密钥
// 参数:
//   - size: 密钥长度 (16/24/32 字节对应 AES-128/192/256)
// 返回:
//   - 生成的随机密钥
//   - 错误信息
func GenerateKey(size int) ([]byte, error) {
    // 验证密钥大小是否有效
    if size != 16 && size != 24 && size != 32 {
        return nil, fmt.Errorf("无效的密钥大小: %d (必须是 16, 24 或 32 字节)", size)
    }
    
    key := make([]byte, size)
    n, err := rand.Read(key) // 使用密码学安全随机数
    
    // 确保读取了足够的随机字节
    if err != nil {
        return nil, fmt.Errorf("生成随机密钥失败: %v", err)
    }
    if n != size {
        return nil, fmt.Errorf("未能生成足够的随机字节: 需要 %d, 获得 %d", size, n)
    }
    
    return key, nil
}


// 示例用法
func main() {
    // 生成密钥 (32 字节 = 256 位)
    key, err := GenerateKey(32)
    if err != nil {
        fmt.Printf("密钥生成失败: %v\n", err)
        return
    }

    // 示例明文
    plaintext := "Hello, this is a secret message!"

    // 加密
    ciphertextBase64, err := AESGCMEncrypt(key, plaintext)
    if err != nil {
        fmt.Printf("加密失败: %v\n", err)
        return
    }
    fmt.Printf("加密后的密文(Base64 编码): %s\n", ciphertextBase64)

    // 解密
    decryptedText, err := AESGCMDecrypt(key, ciphertextBase64)
    if err != nil {
        fmt.Printf("解密失败: %v\n", err)
        return
    }
    fmt.Printf("解密后的明文: %s\n", decryptedText)
}

使用说明

  1. 密钥生成

    • 使用 GenerateKey(size) 函数生成密钥
    • 支持 16/24/32 字节密钥,对应 AES-128/192/256
  2. 加密

    • 调用 AESGCMEncrypt(key, plaintext) 函数
    • 返回 Base64 编码的密文
  3. 解密

    • 调用 AESGCMDecrypt(key, ciphertextBase64) 函数
    • 返回解密后的明文

安全注意事项

  1. 密钥管理

    • 安全存储密钥,避免泄露
    • 考虑使用密钥派生函数 (KDF) 从密码生成密钥
    • 定期轮换密钥
  2. Nonce 处理

    • 本实现自动生成随机 nonce 并附加到密文
    • 确保每次加密使用不同的 nonce
  3. 认证数据

    • 当前实现未使用附加认证数据 (AAD)
    • 如需更高安全性,可在 SealOpen 方法中添加 AAD
  4. 错误处理

    • 始终检查返回的错误
    • 避免泄露敏感错误信息给用户

适用场景

  • 敏感数据加密存储
  • 安全通信
  • 令牌/凭证加密
  • 配置文件加密
每日更新-免费小火箭账号
不要错过任何机会,探索最新的应用和游戏,就在我们的平台。
立即访问
最后修改:2025 年 04 月 12 日
如果觉得我的文章对你有用,请随意赞赏