Go 语言标准库 —— crypto 包(密码学)
🔹 概述
crypto 包及其子包实现了常见的密码学算法,包括哈希、加密、随机数生成等。
主要子包:
- crypto/md5 - MD5 哈希(不推荐用于安全场景)
- crypto/sha1 - SHA-1 哈希(不推荐用于安全场景)
- crypto/sha256 - SHA-256 和 SHA-224 哈希
- crypto/sha512 - SHA-512 哈希族
- crypto/hmac - HMAC 消息认证码
- crypto/aes - AES 加密
- crypto/des - DES 加密(已不推荐)
- crypto/rc4 - RC4 加密(已不推荐)
- crypto/rand - 密码学安全的随机数生成
- crypto/elliptic - 椭圆曲线算法
- crypto/tls - TLS/SSL 协议
- crypto/x509 - X.509 证书处理
- crypto/ed25519 - Ed25519 签名
- crypto/rsa - RSA 加密和签名
- crypto/ecdsa - ECDSA 签名
重要说明:
- 使用标准库提供的密码学算法,不要自己实现
- 选择经过验证的安全算法(如 SHA-256、AES)
- 避免使用已破解的算法(如 MD5、SHA-1、DES、RC4)
- 使用 crypto/rand 而不是 math/rand 生成密码学随机数
🔹 哈希函数
MD5 哈希(不推荐用于安全场景)
crypto/md5
-
说明:
- 实现 MD5 哈希算法
- 生成 128 位(16 字节)哈希值
- ⚠️ 已破解,不推荐用于安全场景
- 仍可用于校验和等非安全场景
-
示例:
package main import ( "crypto/md5" "encoding/hex" "fmt" ) func main() { data := []byte("Hello, World!") hash := md5.Sum(data) hashStr := hex.EncodeToString(hash[:]) fmt.Printf("MD5: %s\n", hashStr) // 输出:65a8e27d8879283831b664bd8b7f0ad4 }
SHA-1 哈希(不推荐用于安全场景)
crypto/sha1
-
说明:
- 实现 SHA-1 哈希算法
- 生成 160 位(20 字节)哈希值
- ⚠️ 已破解,不推荐用于安全场景
- 仅用于兼容旧系统
-
示例:
package main import ( "crypto/sha1" "encoding/hex" "fmt" ) func main() { data := []byte("Hello, World!") hash := sha1.Sum(data) hashStr := hex.EncodeToString(hash[:]) fmt.Printf("SHA-1: %s\n", hashStr) // 输出:0a0a9f2a6772942557ab5355d76af442f8f65e01 }
SHA-256 哈希(推荐)
crypto/sha256
-
说明:
- 实现 SHA-256 和 SHA-224 哈希算法
- SHA-256 生成 256 位(32 字节)哈希值
- SHA-224 生成 224 位(28 字节)哈希值
- ✅ 推荐使用,安全性高
-
示例:
package main import ( "crypto/sha256" "encoding/hex" "fmt" ) func main() { data := []byte("Hello, World!") // SHA-256 hash256 := sha256.Sum256(data) fmt.Printf("SHA-256: %s\n", hex.EncodeToString(hash256[:])) // SHA-224 hash224 := sha256.Sum224(data) fmt.Printf("SHA-224: %s\n", hex.EncodeToString(hash224[:])) }
SHA-512 哈希(推荐)
crypto/sha512
-
说明:
- 实现 SHA-512、SHA-384、SHA-512/224、SHA-512/256 哈希算法
- SHA-512 生成 512 位(64 字节)哈希值
- ✅ 推荐使用,安全性最高
-
示例:
package main import ( "crypto/sha512" "encoding/hex" "fmt" ) func main() { data := []byte("Hello, World!") // SHA-512 hash512 := sha512.Sum512(data) fmt.Printf("SHA-512: %s\n", hex.EncodeToString(hash512[:])) // SHA-384 hash384 := sha512.Sum384(data) fmt.Printf("SHA-384: %s\n", hex.EncodeToString(hash384[:])) }
哈希接口(通用方式)
crypto.Hash
-
说明:
- 提供统一的哈希接口
- 可以使用 New() 方法创建哈希器
- 支持多种哈希算法
-
示例:
package main import ( "crypto" "encoding/hex" "fmt" "io" ) func main() { data := []byte("Hello, World!") // 使用哈希接口 h := crypto.SHA256.New() io.WriteString(h, string(data)) hash := h.Sum(nil) fmt.Printf("SHA-256: %s\n", hex.EncodeToString(hash)) }
🔹 HMAC 消息认证码
HMAC
crypto/hmac
-
说明:
- 实现 HMAC(Keyed-Hash Message Authentication Code)
- 结合密钥和哈希算法
- 用于验证消息的完整性和真实性
- 必须与哈希算法配合使用(如 HMAC-SHA256)
-
示例(HMAC-SHA256):
package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" ) func computeHMAC(data []byte, key []byte) string { h := hmac.New(sha256.New, key) h.Write(data) return hex.EncodeToString(h.Sum(nil)) } func verifyHMAC(data []byte, key []byte, expectedMAC string) bool { actualMAC := computeHMAC(data, key) return hmac.Equal([]byte(actualMAC), []byte(expectedMAC)) } func main() { data := []byte("message to authenticate") key := []byte("secret key") // 计算 HMAC mac := computeHMAC(data, key) fmt.Printf("HMAC: %s\n", mac) // 验证 HMAC valid := verifyHMAC(data, key, mac) fmt.Printf("验证结果:%v\n", valid) } -
注意:
- 使用
hmac.Equal()比较 HMAC 值(防止时序攻击) - 密钥应该足够长且随机
- 使用
🔹 对称加密
AES 加密(推荐)
crypto/aes
-
说明:
- 实现 AES(Advanced Encryption Standard)加密
- 支持 128、192、256 位密钥
- ✅ 推荐使用,安全性高
- 需要配合工作模式(如 CBC、GCM)
-
示例(AES-GCM,推荐):
package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/hex" "fmt" "io" ) // AES-GCM 加密 func encryptAES(plaintext []byte, key []byte) ([]byte, error) { // 创建 AES cipher block, err := aes.NewCipher(key) if err != nil { return nil, err } // 创建 GCM mode 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 } // AES-GCM 解密 func decryptAES(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, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) return plaintext, err } func main() { // 256 位密钥(32 字节) key := []byte("12345678901234567890123456789012") plaintext := []byte("Hello, World!") // 加密 ciphertext, err := encryptAES(plaintext, key) if err != nil { fmt.Println("加密失败:", err) return } fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext)) // 解密 decrypted, err := decryptAES(ciphertext, key) if err != nil { fmt.Println("解密失败:", err) return } fmt.Printf("明文:%s\n", string(decrypted)) } -
AES-CBC 示例:
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) { return nil, fmt.Errorf("无效的填充") } return data[:len(data)-padding], nil } // AES-CBC 加密 func encryptAES_CBC(plaintext []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } // PKCS7 填充 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 } // AES-CBC 解密 func decryptAES_CBC(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:] // 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, World!") ciphertext, _ := encryptAES_CBC(plaintext, key) fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext)) decrypted, _ := decryptAES_CBC(ciphertext, key) fmt.Printf("明文:%s\n", string(decrypted)) }
🔹 非对称加密
RSA 加密和签名
crypto/rsa
-
说明:
- 实现 RSA 加密和签名
- 支持密钥生成、加密、解密、签名、验证
- 密钥长度建议至少 2048 位
-
示例(RSA 加密/解密):
package main import ( "crypto/rand" "crypto/rsa" "crypto/sha256" "encoding/hex" "fmt" ) func main() { // 生成 RSA 密钥对(2048 位) privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) publicKey := &privateKey.PublicKey plaintext := []byte("Hello, RSA!") // 加密(使用公钥) ciphertext, _ := rsa.EncryptOAEP( sha256.New(), rand.Reader, publicKey, plaintext, nil, ) fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext)) // 解密(使用私钥) decrypted, _ := rsa.DecryptOAEP( sha256.New(), rand.Reader, privateKey, ciphertext, nil, ) fmt.Printf("明文:%s\n", string(decrypted)) } -
示例(RSA 签名/验证):
package main import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "encoding/hex" "fmt" ) func main() { // 生成密钥对 privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) publicKey := &privateKey.PublicKey message := []byte("Message to sign") // 计算消息哈希 hash := sha256.Sum256(message) // 签名(使用私钥) signature, _ := rsa.SignPSS( rand.Reader, privateKey, crypto.SHA256, hash[:], nil, ) fmt.Printf("签名:%s\n", hex.EncodeToString(signature)) // 验证签名(使用公钥) err := rsa.VerifyPSS( publicKey, crypto.SHA256, hash[:], signature, nil, ) if err == nil { fmt.Println("签名验证通过") } else { fmt.Println("签名验证失败") } }
ECDSA 签名
crypto/ecdsa
-
说明:
- 实现 ECDSA(Elliptic Curve Digital Signature Algorithm)
- 比 RSA 更高效的签名算法
- 需要配合椭圆曲线使用
-
示例:
package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "math/big" ) func main() { // 生成 ECDSA 密钥对(P-256 曲线) privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) publicKey := &privateKey.PublicKey message := []byte("Message to sign") // 计算消息哈希 hash := sha256.Sum256(message) // 签名 r, s, _ := ecdsa.Sign(rand.Reader, privateKey, hash[:]) fmt.Printf("签名 R: %s\n", r.Text(16)) fmt.Printf("签名 S: %s\n", s.Text(16)) // 验证签名 valid := ecdsa.Verify(publicKey, hash[:], r, s) fmt.Printf("验证结果:%v\n", valid) }
Ed25519 签名(推荐)
crypto/ed25519
-
说明:
- 实现 Ed25519 签名算法
- 高性能、高安全性
- ✅ 推荐使用
-
示例:
package main import ( "crypto/ed25519" "crypto/rand" "encoding/hex" "fmt" ) func main() { // 生成 Ed25519 密钥对 publicKey, privateKey, _ := ed25519.GenerateKey(rand.Reader) message := []byte("Message to sign") // 签名 signature := ed25519.Sign(privateKey, message) fmt.Printf("签名:%s\n", hex.EncodeToString(signature)) // 验证签名 valid := ed25519.Verify(publicKey, message, signature) fmt.Printf("验证结果:%v\n", valid) }
🔹 随机数生成
密码学安全的随机数
crypto/rand
-
说明:
- 提供密码学安全的随机数生成
- ✅ 必须用于所有密码学场景
- 不要使用 math/rand 生成密码学随机数
-
示例:
package main import ( "crypto/rand" "encoding/hex" "fmt" "math/big" ) func main() { // 生成随机字节 randomBytes := make([]byte, 32) rand.Read(randomBytes) fmt.Printf("随机字节:%s\n", hex.EncodeToString(randomBytes)) // 生成随机大整数 max := new(big.Int).Exp(big.NewInt(10), big.NewInt(10), nil) randomInt, _ := rand.Int(rand.Reader, max) fmt.Printf("随机整数:%s\n", randomInt.String()) // 生成随机数用于 OTP 等 otp := make([]byte, 6) rand.Read(otp) fmt.Printf("OTP: %x\n", otp) }
🔹 椭圆曲线
椭圆曲线算法
crypto/elliptic
-
说明:
- 提供标准椭圆曲线实现
- 支持 P-224、P-256、P-384、P-521 曲线
- 用于 ECDH 密钥交换和 ECDSA 签名
-
示例(ECDH 密钥交换):
package main import ( "crypto/ecdh" "crypto/rand" "encoding/hex" "fmt" ) func main() { // 生成 Alice 的密钥对 alicePriv, _ := ecdh.P256().GenerateKey(rand.Reader) alicePub := alicePriv.PublicKey() // 生成 Bob 的密钥对 bobPriv, _ := ecdh.P256().GenerateKey(rand.Reader) bobPub := bobPriv.PublicKey() // Alice 计算共享密钥 aliceShared, _ := alicePriv.ECDH(bobPub) // Bob 计算共享密钥 bobShared, _ := bobPriv.ECDH(alicePub) // 验证共享密钥相同 fmt.Printf("Alice 共享密钥:%s\n", hex.EncodeToString(aliceShared)) fmt.Printf("Bob 共享密钥:%s\n", hex.EncodeToString(bobShared)) fmt.Printf("密钥相同:%v\n", string(aliceShared) == string(bobShared)) }
🔹 使用场景
1. 密码哈希存储
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
)
// 使用 bcrypt 更好,这里仅做示例
func hashPassword(password string) (string, string, error) {
// 生成随机盐
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return "", "", err
}
// 加盐哈希
hash := sha256.Sum256(append([]byte(password), salt...))
return hex.EncodeToString(hash[:]), hex.EncodeToString(salt), nil
}
func verifyPassword(password, storedHash, storedSalt string) bool {
salt, _ := hex.DecodeString(storedSalt)
hash := sha256.Sum256(append([]byte(password), salt...))
return hex.EncodeToString(hash[:]) == storedHash
}
func main() {
password := "mySecurePassword123"
hash, salt, _ := hashPassword(password)
fmt.Printf("哈希:%s\n", hash)
fmt.Printf("盐:%s\n", salt)
// 验证密码
valid := verifyPassword(password, hash, salt)
fmt.Printf("验证结果:%v\n", valid)
}
2. JWT 令牌签名
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)
type Header struct {
Alg string `json:"alg"`
Typ string `json:"typ"`
}
type Claims struct {
UserID string `json:"user_id"`
Exp int64 `json:"exp"`
}
func base64Encode(data []byte) string {
return strings.TrimRight(base64.URLEncoding.EncodeToString(data), "=")
}
func createJWT(header Header, claims Claims, secret string) (string, error) {
// 编码 header 和 claims
headerJSON, _ := json.Marshal(header)
claimsJSON, _ := json.Marshal(claims)
headerB64 := base64Encode(headerJSON)
claimsB64 := base64Encode(claimsJSON)
// 创建签名输入
signatureInput := headerB64 + "." + claimsB64
// 计算 HMAC-SHA256 签名
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(signatureInput))
signature := h.Sum(nil)
signatureB64 := base64Encode(signature)
return signatureInput + "." + signatureB64, nil
}
func main() {
header := Header{Alg: "HS256", Typ: "JWT"}
claims := Claims{UserID: "123", Exp: 9999999999}
secret := "my-secret-key"
token, _ := createJWT(header, claims, secret)
fmt.Printf("JWT: %s\n", token)
}
3. 文件完整性校验
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
)
func hashFile(filename string) (string, error) {
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func verifyFile(filename, expectedHash string) (bool, error) {
actualHash, err := hashFile(filename)
if err != nil {
return false, err
}
return actualHash == expectedHash, nil
}
func main() {
hash, _ := hashFile("important.zip")
fmt.Printf("文件哈希:%s\n", hash)
// 验证文件完整性
valid, _ := verifyFile("important.zip", hash)
fmt.Printf("完整性验证:%v\n", valid)
}
🔹 注意事项和最佳实践
1. 选择安全的算法
-
✅ 推荐使用的算法:
- 哈希:SHA-256、SHA-384、SHA-512
- 加密:AES-256-GCM
- 签名:Ed25519、ECDSA、RSA(2048+ 位)
- 随机数:crypto/rand
-
❌ 避免使用的算法:
- 哈希:MD5、SHA-1(已破解)
- 加密:DES、RC4、3DES(已不推荐)
- 随机数:math/rand(非密码学安全)
2. 密钥管理
-
✅ 使用足够长的密钥
- AES:至少 256 位(32 字节)
- RSA:至少 2048 位
- ECDSA:至少 P-256 曲线
-
✅ 安全存储密钥
- 不要硬编码密钥
- 使用密钥管理服务(KMS)
- 使用环境变量或配置文件
3. 使用 GCM 模式
- ✅ AES 优先使用 GCM 模式
- 提供认证加密
- 防止篡改
- 性能优秀
// 推荐
gcm, _ := cipher.NewGCM(block)
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// 不推荐(仅加密无认证)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
4. 密码哈希
- ✅ 使用专门的密码哈希函数
- bcrypt(golang.org/x/crypto/bcrypt)
- scrypt
- argon2
import "golang.org/x/crypto/bcrypt"
// 哈希密码
hashedPassword, _ := bcrypt.GenerateFromPassword(
[]byte(password),
bcrypt.DefaultCost,
)
// 验证密码
err := bcrypt.CompareHashAndPassword(
[]byte(hashedPassword),
[]byte(password),
)
5. 随机数生成
- ✅ 始终使用 crypto/rand
- ❌ 不要使用 math/rand 用于密码学
// 正确
import "crypto/rand"
randomBytes := make([]byte, 32)
rand.Read(randomBytes)
// 错误 - 非密码学安全
import "math/rand"
randomBytes := make([]byte, 32)
rand.Read(randomBytes) // 可预测!
6. 常量时间比较
- ✅ 使用 hmac.Equal 比较敏感数据
- ❌ 不要使用 == 比较 HMAC 或密码
// 正确
if hmac.Equal(providedMAC, expectedMAC) {
// 验证通过
}
// 错误 - 可能遭受时序攻击
if providedMAC == expectedMAC {
// 不安全!
}
🔥 总结
核心子包
| 子包 | 说明 | 推荐度 |
|---|---|---|
| crypto/md5 | MD5 哈希 | ❌ 不推荐(已破解) |
| crypto/sha1 | SHA-1 哈希 | ❌ 不推荐(已破解) |
| crypto/sha256 | SHA-256 哈希 | ✅ 推荐 |
| crypto/sha512 | SHA-512 哈希 | ✅ 推荐 |
| crypto/hmac | HMAC 认证码 | ✅ 推荐 |
| crypto/aes | AES 加密 | ✅ 推荐 |
| crypto/rand | 安全随机数 | ✅ 推荐 |
| crypto/rsa | RSA 加密/签名 | ✅ 推荐 |
| crypto/ecdsa | ECDSA 签名 | ✅ 推荐 |
| crypto/ed25519 | Ed25519 签名 | ✅ 强烈推荐 |
算法选择指南
哈希算法:
- ✅ SHA-256 - 通用场景
- ✅ SHA-512 - 高安全性场景
- ❌ MD5/SHA-1 - 仅用于校验和
加密算法:
- ✅ AES-256-GCM - 对称加密
- ✅ RSA-2048+ - 非对称加密
- ❌ DES/RC4 - 已不推荐
签名算法:
- ✅ Ed25519 - 高性能签名
- ✅ ECDSA - 椭圆曲线签名
- ✅ RSA - 传统签名
最佳实践
- ✅ 使用标准库,不要自己实现密码学
- ✅ 选择经过验证的安全算法
- ✅ 使用足够长的密钥
- ✅ 使用 crypto/rand 生成随机数
- ✅ 使用 hmac.Equal 比较敏感数据
- ✅ 密码哈希使用 bcrypt/scrypt
- ✅ AES 优先使用 GCM 模式
- ⚠️ 注意:妥善管理密钥
安全建议
- 🔒 定期更新依赖和算法
- 🔒 使用密钥管理服务
- 🔒 实施密钥轮换
- 🔒 记录密码学操作日志
- 🔒 进行安全审计
crypto 包提供了全面的密码学功能,请始终选择安全的算法并遵循最佳实践!