加密和解密是保护数据安全的核心,无论您是在构建 Web 应用程序、CLI 工具还是后端服务。在 Go 语言中,标准库和外部包使得实现安全加密变得简单,无需重新发明轮子。本指南将深入探讨 Go 中的加密和解密工作原理,并提供可编译和运行的实际示例。我们将涵盖从对称加密到非对称加密的基础知识,并附有清晰的代码和解释。

为什么加密在 Go 中很重要

加密通过将敏感数据(如用户凭据或支付信息)转换为不可读的密文来保护数据安全。解密则将这一过程逆转,仅限授权用户使用。Go 的 crypto 包提供了强大的工具,兼顾了安全性和简便性。无论您是在保护 API 负载还是存储敏感配置文件,Go 的加密支持都能满足您的需求。

AES 理解对称加密

对称加密是一种使用单一密钥进行加密和解密的技术,广泛用于保护数据机密性。高级加密标准(AES)因其高效性和安全性成为主流选择。Go 的 crypto/aes 包提供了便捷的实现方式,支持 AES-CBC(密码块链接模式)等模式。AES 使用固定大小的密钥(128、192 或 256 位)对数据块(128 位)进行加密。为了提升安全性,每次加密都会生成一个随机的初始化向量(IV)。密钥的安全共享是对称加密的关键。

以下是一个完整的示例,使用 AES-256 的 CBC 模式。它加密一个明文字符串并将其解密回来。

package main

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

// 主函数入口
func main() {
    plaintext := []byte("Hello, FunTester encryption!") // 明文内容
    key := []byte("32-byte-key-for-AES-256-!!!!!!!!")  // 32 字节密钥,用于 AES-256

    // 加密
    ciphertext, err := encryptAES(plaintext, key)
    if err != nil {
        fmt.Println("加密错误:", err)
        return
    }
    fmt.Printf("密文 (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))

    // 解密
    decrypted, err := decryptAES(ciphertext, key)
    if err != nil {
        fmt.Println("解密错误:", err)
        return
    }
    fmt.Printf("解密结果: %s\n", decrypted)
}

// AES 加密函数,使用 CBC 模式
func encryptAES(plaintext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key) // 创建 AES 密码块
    if err != nil {
        return nil, err
    }

    // 填充明文,保证长度为块大小的整数倍
    padding := aes.BlockSize - len(plaintext)%aes.BlockSize
    padtext := append(plaintext, bytes.Repeat([]byte{byte(padding)}, padding)...)

    ciphertext := make([]byte, aes.BlockSize+len(padtext)) // 分配密文空间
    iv := ciphertext[:aes.BlockSize] // 生成初始化向量 IV
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    mode := cipher.NewCBCEncrypter(block, iv) // 创建 CBC 加密器
    mode.CryptBlocks(ciphertext[aes.BlockSize:], padtext) // 加密数据

    return ciphertext, nil
}

// AES 解密函数,使用 CBC 模式
func decryptAES(ciphertext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key) // 创建 AES 密码块
    if err != nil {
        return nil, err
    }

    if len(ciphertext) < aes.BlockSize {
        return nil, fmt.Errorf("密文长度不足")
    }

    iv := ciphertext[:aes.BlockSize] // 取出初始化向量 IV
    ciphertext = ciphertext[aes.BlockSize:] // 取出实际密文

    mode := cipher.NewCBCDecrypter(block, iv) // 创建 CBC 解密器
    mode.CryptBlocks(ciphertext, ciphertext) // 解密数据

    // 去除填充,恢复原始明文
    padding := int(ciphertext[len(ciphertext)-1])
    return ciphertext[:len(ciphertext)-padding], nil
}

// 输出示例:
// 密文 (base64): [随机生成的 base64 字符串]
// 解密结果: Hello, FunTester encryption!

探索 AES-GCM 的认证加密

AES-GCM(Galois/计数器模式)是一种高级加密模式,兼顾数据机密性、完整性和真实性。与 CBC 模式不同,GCM 不需要手动填充,并通过内置标签提供认证功能。它非常适合安全通信场景,如 HTTPS 和 API 令牌加密。Go 的 crypto/cipher 包原生支持 GCM,使开发者能够轻松实现认证加密。GCM 的随机数(Nonce)确保每次加密唯一,避免重放攻击,同时支持可变长度数据,提升了灵活性和安全性。

以下是一个完整的示例,展示如何使用 AES-GCM 进行加密和解密。

package main

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

func main() {
    plaintext := []byte("Secure data with GCM!")
    key := []byte("32-byte-key-for-AES-256-!!!!!!!!") // 32 bytes for AES-256

    // Encrypt
    ciphertext, err := encryptGCM(plaintext, key)
    if err != nil {
        fmt.Println("加密错误:", err)
        return
    }
    fmt.Printf("密文 (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))

    // Decrypt
    decrypted, err := decryptGCM(ciphertext, key)
    if err != nil {
        fmt.Println("解密错误:", err)
        return
    }
    fmt.Printf("解密结果: %s\n", decrypted)
}

func encryptGCM(plaintext, 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
    }

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

    return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

func decryptGCM(ciphertext, 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, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    return gcm.Open(nil, nonce, ciphertext, nil)
}

// 输出示例:
// 密文 (base64): [随机生成的 base64 字符串]
// 解密结果: Secure data with GCM!

RSA 的非对称加密

非对称加密使用公钥加密、私钥解密,适合安全密钥交换或小数据加密。RSA 是常用算法,Go 的 crypto/rsa 包提供支持。由于速度较慢,RSA 通常与对称加密结合使用,形成混合加密系统,既保证安全性又提升效率。

此示例生成一个 RSA 密钥对,使用公钥加密消息,并使用私钥解密。

package main

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

func main() {
    // 生成密钥对
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        fmt.Println("密钥生成错误:", err)
        return
    }
    publicKey := &privateKey.PublicKey

    // 加密
    plaintext := []byte("RSA encryption in Go!")
    ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, plaintext, nil)
    if err != nil {
        fmt.Println("加密错误:", err)
        return
    }
    fmt.Printf("密文 (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))

    // 解密
    decrypted, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, ciphertext, nil)
    if err != nil {
        fmt.Println("解密错误:", err)
        return
    }
    fmt.Printf("解密结果: %s\n", decrypted)
}

// 输出示例:
// 密文 (base64): [随机生成的 base64 字符串]
// 解密结果: RSA encryption in Go!

哈希与加密:有什么区别?

哈希和加密经常被混淆,但它们的用途不同。哈希是单向的,用于完整性检查(例如密码),而加密是可逆的,用于机密性。

特性 加密 哈希
目的 保护数据机密性 验证数据完整性
可逆性 是(需要密钥) 否(单向)
Go 包 crypto/aes, crypto/rsa crypto/sha256, crypto/md5
示例用途 保护 API 负载 密码存储

示例:使用 SHA-256 进行哈希

以下是如何使用 SHA-256 对字符串进行哈希。

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    data := []byte("Hash this string!")
    hash := sha256.Sum256(data)
    fmt.Printf("SHA-256 Hash: %s\n", hex.EncodeToString(hash[:]))

    // 输出示例:
    // SHA-256 Hash: 2c7a6e66323c8f7a0e205803c763eb8a4e8b6f8b0b2c3f8a7e8f9d0b1e2c3d4e
}

示例:生成安全密钥

package main

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

func main() {
    key := make([]byte, 32) // 32 bytes for AES-256
    _, err := rand.Read(key)
    if err != nil {
        fmt.Println("密钥生成错误:", err)
        return
    }
    fmt.Printf("生成的密钥: %s\n", hex.EncodeToString(key))

    // 输出示例:
    // 生成的密钥: [随机生成的 64 字符十六进制字符串]
}

FunTester 原创精华


↙↙↙阅读原文可查看相关链接,并与作者交流