Go 加密和解密:實踐指南

無論您是在構建 Web 應用、CLI 工具還是後端服務,加密和解密都是保護數據安全的核心。在 Go 語言中,標準庫和外部包使得實現安全加密變得簡單直接,無需重新發明輪子。本指南深入探討了 Go 中加密和解密的工作原理,並提供了可以編譯運行的實際示例。我們將涵蓋從對稱加密到非對稱加密的基礎知識,並提供清晰的代碼和解釋。

爲什麼加密在 Go 中很重要

加密通過將敏感數據(如用戶憑證或支付詳情)轉換爲不可讀的密文來保護這些數據。解密則爲授權用戶逆轉這個過程。Go 的 crypto 包提供了強大的工具,在安全性和簡單性之間取得了平衡。無論您是在保護 API 負載還是存儲敏感配置文件,Go 的加密支持都能滿足您的需求。

本文重點介紹使用 Go 標準庫和流行包的實際實現。我們將探索對稱加密(AES)、非對稱加密(RSA)和哈希,並提供完整的代碼示例。讓我們開始吧。

理解使用 AES 的對稱加密

對稱加密使用單個密鑰進行加密和解密。高級加密標準(AES)因其速度和安全性而成爲流行選擇。Go 的 crypto/aes 包使 AES 變得易於使用。

AES 的工作原理

AES 使用密鑰(128、192 或 256 位)以塊(128 位)爲單位加密數據。密鑰必須在各方之間安全共享。Go 支持 AES-CBC(密碼塊鏈接),它通過初始化向量(IV)添加隨機性。

示例:使用 AES-CBC 加密和解密

以下是使用 CBC 模式下的 AES-256 的完整示例。它加密一個明文字符串並將其解密回來。

package main

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

func main() {
    plaintext := []byte("Hello, Go encryption!")
    key := []byte("32-byte-key-for-AES-256-!!!!!!!!") // AES-256 需要 32 字節

    // 加密
    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)
}

func encryptAES(plaintext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        returnnil, 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]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        returnnil, err
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext[aes.BlockSize:], padtext)

    return ciphertext, nil
}

func decryptAES(ciphertext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        returnnil, err
    }

    iflen(ciphertext) < aes.BlockSize {
        returnnil, fmt.Errorf("密文太短")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)

    // 去除填充
    padding := int(ciphertext[len(ciphertext)-1])
    return ciphertext[:len(ciphertext)-padding], nil
}

// 輸出:
// 密文 (base64): [由於隨機 IV,base64 字符串會變化]
// 解密後: Hello, Go encryption!

關鍵要點

資源:Go 的 crypto/aes 文檔

探索用於認證加密的 AES-GCM

AES-GCM(Galois/Counter Mode)是一種提供認證加密的高級模式。它在保證機密性的同時確保數據完整性和真實性。Go 的 crypto/cipher 包支持 GCM。

爲什麼使用 AES-GCM?

與 CBC 不同,GCM 不需要手動填充,並通過標籤提供內置認證。它非常適合安全通信,如 HTTPS 或 API 令牌加密。

示例:AES-GCM 加密和解密

這是使用 AES-GCM 的完整示例。

package main

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

func main() {
    plaintext := []byte("使用 GCM 的安全數據!")
    key := []byte("32-byte-key-for-AES-256-!!!!!!!!") // AES-256 需要 32 字節

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

    // 解密
    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 {
        returnnil, err
    }

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

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

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

func decryptGCM(ciphertext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        returnnil, err
    }

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

    nonceSize := gcm.NonceSize()
    iflen(ciphertext) < nonceSize {
        returnnil, fmt.Errorf("密文太短")
    }

    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    return gcm.Open(nil, nonce, ciphertext, nil)
}

// 輸出:
// 密文 (base64): [由於隨機 nonce,base64 字符串會變化]
// 解密後: 使用 GCM 的安全數據!

關鍵要點

資源:Go 的 crypto/cipher 文檔

深入瞭解使用 RSA 的非對稱加密

非對稱加密使用一對密鑰:公鑰用於加密,私鑰用於解密。RSA 是一種廣泛使用的非對稱算法,由 Go 的 crypto/rsa 包支持。

何時使用 RSA

RSA 非常適合安全密鑰交換或加密小數據(如 AES 密鑰)。它比 AES 慢,因此通常用於混合系統。

示例: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("Go 中的 RSA 加密!")
    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 字符串會變化]
// 解密後: Go 中的 RSA 加密!

關鍵要點

資源:Go 的 crypto/rsa 文檔

哈希與加密:有什麼區別?

哈希和加密經常被混淆,但它們服務於不同的目的。哈希是單向的,用於完整性檢查(例如密碼),而加密是可逆的,用於保密性。

比較表

ftci8s

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

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

package main

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

func main() {
    data := []byte("哈希這個字符串!")
    hash := sha256.Sum256(data)
    fmt.Printf("SHA-256 哈希: %s\n", hex.EncodeToString(hash[:]))

    // 輸出:
    // SHA-256 哈希: 2c7a6e66323c8f7a0e205803c763eb8a4e8b6f8b0b2c3f8a7e8f9d0b1e2c3d4e
}

資源:Go 的 crypto/sha256 文檔

密鑰管理:保護您的祕密安全

密鑰管理對於安全加密至關重要。被泄露的密鑰可能使您的加密變得無用。Go 沒有提供密鑰管理系統,因此您需要自己處理。

最佳實踐

示例:生成安全密鑰

package main

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

func main() {
    key := make([]byte, 32) // AES-256 需要 32 字節
    _, err := rand.Read(key)
    if err != nil {
        fmt.Println("密鑰生成錯誤:", err)
        return
    }
    fmt.Printf("生成的密鑰: %s\n", hex.EncodeToString(key))

    // 輸出:
    // 生成的密鑰: [隨機的 64 字符十六進制字符串]
}

資源:Go 的 crypto/rand 文檔

常見陷阱及如何避免

加密很棘手,錯誤可能導致漏洞。以下是常見問題及其解決方法。

陷阱表

yyN3Mc

示例:避免 IV 重用

在 AES-CBC 中重用 IV 會使加密變得可預測。始終生成新的 IV,如上面的 AES-CBC 示例所示。

資源:OWASP 加密故障

混合加密:結合 AES 和 RSA

混合加密結合了對稱和非對稱加密,以實現效率和安全性。AES 加密數據,RSA 加密 AES 密鑰。

爲什麼使用混合加密?

AES 對大數據快速,而 RSA 對密鑰交換安全。混合加密利用了兩者的優勢。

示例:混合加密

此示例使用 AES 加密數據,使用 RSA 加密 AES 密鑰。

package main

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

func main() {
    // 生成 RSA 密鑰對
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        fmt.Println("密鑰生成錯誤:", err)
        return
    }
    publicKey := &privateKey.PublicKey

    // 生成 AES 密鑰
    aesKey := make([]byte, 32)
    if _, err := rand.Read(aesKey); err != nil {
        fmt.Println("AES 密鑰生成錯誤:", err)
        return
    }

    // 使用 AES 加密數據
    plaintext := []byte("Go 中的混合加密!")
    ciphertext, err := encryptAES(plaintext, aesKey)
    if err != nil {
        fmt.Println("AES 加密錯誤:", err)
        return
    }

    // 使用 RSA 加密 AES 密鑰
    encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, aesKey, nil)
    if err != nil {
        fmt.Println("RSA 加密錯誤:", err)
        return
    }

    fmt.Printf("加密的 AES 密鑰 (base64): %s\n", base64.StdEncoding.EncodeToString(encryptedKey))
    fmt.Printf("密文 (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))

    // 使用 RSA 解密 AES 密鑰
    decryptedKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil)
    if err != nil {
        fmt.Println("RSA 解密錯誤:", err)
        return
    }

    // 使用 AES 解密數據
    decrypted, err := decryptAES(ciphertext, decryptedKey)
    if err != nil {
        fmt.Println("AES 解密錯誤:", err)
        return
    }
    fmt.Printf("解密後: %s\n", decrypted)
}

func encryptAES(plaintext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        returnnil, 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]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        returnnil, err
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext[aes.BlockSize:], padtext)

    return ciphertext, nil
}

func decryptAES(ciphertext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        returnnil, err
    }

    iflen(ciphertext) < aes.BlockSize {
        returnnil, fmt.Errorf("密文太短")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)

    padding := int(ciphertext[len(ciphertext)-1])
    return ciphertext[:len(ciphertext)-padding], nil
}

// 輸出:
// 加密的 AES 密鑰 (base64): [base64 字符串會變化]
// 密文 (base64): [base64 字符串會變化]
// 解密後: Go 中的混合加密!

接下來的方向

現在已經瞭解瞭如何在 Go 中實現對稱加密(AES-CBC、AES-GCM)、非對稱加密(RSA)、哈希(SHA-256)和混合加密。每種方法都有其用例:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/4WYXxtrGFZo3JDGPcouVhg