加密与安全
包总览
| 包 | 用途 | 分类 |
|---|
crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 | 哈希算法 | 哈希 |
crypto/hmac | HMAC 消息认证 | 认证 |
crypto/aes crypto/des | 对称加密 | 对称加密 |
crypto/rsa crypto/ecdsa crypto/ed25519 | 非对称加密 | 非对称 |
crypto/cipher | 加密模式(GCM/CBC/CTR) | 加密模式 |
crypto/rand | 密码学安全随机数 | 随机数 |
crypto/tls | TLS/SSL | 传输安全 |
crypto/x509 | X.509 证书 | 证书 |
encoding/pem | PEM 编码 | 编码 |
哈希算法
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
)
// MD5(不推荐用于安全场景,仅用于校验)
h := md5.Sum([]byte("hello"))
hex.EncodeToString(h[:]) // "5d41402abc4b2a76b9719d911017c592"
// SHA-1(不推荐用于安全场景)
h := sha1.Sum([]byte("hello"))
hex.EncodeToString(h[:]) // "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
// SHA-256(推荐)
h := sha256.Sum256([]byte("hello"))
hex.EncodeToString(h[:]) // "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
// SHA-512
h := sha512.Sum512([]byte("hello"))
hex.EncodeToString(h[:])
// 流式哈希(大文件)
f, _ := os.Open("largefile.iso")
defer f.Close()
h := sha256.New()
io.Copy(h, f)
hash := hex.EncodeToString(h.Sum(nil))
// 多次写入
h := sha256.New()
h.Write([]byte("hello "))
h.Write([]byte("world"))
hash := hex.EncodeToString(h.Sum(nil))
哈希算法对比
| 算法 | 输出长度 | 安全性 | 性能 | 推荐场景 |
|---|
| MD5 | 128 bit | ✗ 已破解 | 最快 | 文件校验(非安全) |
| SHA-1 | 160 bit | ✗ 已破解 | 快 | Git 内部 |
| SHA-256 | 256 bit | ✓ 安全 | 中 | 通用推荐 |
| SHA-512 | 512 bit | ✓ 安全 | 中 | 高安全需求 |
| SHA-3 | 可变 | ✓ 安全 | 慢 | 新标准 |
HMAC
import "crypto/hmac"
// HMAC:用密钥进行消息认证
func ComputeHMAC(key []byte, message []byte) string {
mac := hmac.New(sha256.New, key)
mac.Write(message)
return hex.EncodeToString(mac.Sum(nil))
}
// 验证 HMAC(时间恒定比较,防时序攻击)
func VerifyHMAC(key []byte, message []byte, expectedMAC string) bool {
mac := hmac.New(sha256.New, key)
mac.Write(message)
expected, _ := hex.DecodeString(expectedMAC)
return hmac.Equal(mac.Sum(nil), expected) // 时间恒定比较
}
重要:比较 HMAC 时必须用 hmac.Equal,不要用 == 或 bytes.Equal,否则有时序攻击风险。
对称加密 AES
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
)
// AES-GCM(推荐模式)
func AESEncrypt(key, plaintext []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 := rand.Read(nonce); err != nil {
return nil, err
}
// nonce 前置,加密时自动附加认证标签
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
func AESDecrypt(key, ciphertext []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("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
// 使用
key := make([]byte, 32) // AES-256
rand.Read(key)
encrypted, _ := AESEncrypt(key, []byte("sensitive data"))
decrypted, _ := AESDecrypt(key, encrypted)
fmt.Println(string(decrypted)) // "sensitive data"
AES 加密模式对比
| 模式 | 是否需要 IV/Nonce | 认证 | 并行 | 推荐 |
|---|
| ECB | ✗ | ✗ | ✗ | ✗ 不安全 |
| CBC | ✓ | ✗ | ✓ | ✗ 无认证 |
| CTR | ✓ | ✗ | ✓ | ✗ 无认证 |
| GCM | ✓ | ✓ | ✓ | ✓ 推荐 |
| CFB | ✓ | ✗ | ✗ | ✗ 无认证 |
| OFB | ✓ | ✗ | ✗ | ✗ 无认证 |
关键:始终使用 GCM 模式,它提供加密 + 认证(AEAD),防止篡改。
AES 密钥长度
| AES 变体 | 密钥长度 | 轮数 | 安全性 |
|---|
| AES-128 | 16 字节 | 10 | ✓ |
| AES-192 | 24 字节 | 12 | ✓ |
| AES-256 | 32 字节 | 14 | ✓ |
非对称加密 RSA
import (
"crypto/rsa"
"crypto/rand"
"crypto/x509"
"encoding/pem"
)
// 生成密钥对
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
publicKey := &privateKey.PublicKey
// 导出为 PEM 格式
// 私钥
privBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privBytes,
})
// 公钥
pubBytes := x509.MarshalPKCS1PublicKey(publicKey)
pubPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubBytes,
})
// 从 PEM 解析
block, _ := pem.Decode(privPEM)
priv, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
// 加密
ciphertext, err := rsa.EncryptOAEP(
sha256.New(),
rand.Reader,
publicKey,
[]byte("secret message"),
nil, // label
)
// 解密
plaintext, err := rsa.DecryptOAEP(
sha256.New(),
rand.Reader,
privateKey,
ciphertext,
nil,
)
// 签名
hashed := sha256.Sum256([]byte("message to sign"))
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
// 验签
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signature)
RSA vs ECDSA vs Ed25519
| 算法 | 密钥大小 | 签名速度 | 验签速度 | 安全性 | 推荐度 |
|---|
| RSA-2048 | 2048 bit | 慢 | 快 | ✓ | ★★★☆☆ |
| RSA-4096 | 4096 bit | 很慢 | 中 | ✓ | ★★☆☆☆ |
| ECDSA-P256 | 256 bit | 快 | 快 | ✓ | ★★★★☆ |
| Ed25519 | 256 bit | 最快 | 最快 | ✓ | ★★★★★ |
推荐:新项目使用 Ed25519,已有 RSA 系统至少用 2048 位。
Ed25519
import "crypto/ed25519"
// 生成密钥对
pub, priv, err := ed25519.GenerateKey(rand.Reader)
// 签名
signature := ed25519.Sign(priv, []byte("message"))
// 验签
valid := ed25519.Verify(pub, []byte("message"), signature)
TLS
import "crypto/tls"
// HTTPS 服务端
server := &http.Server{
Addr: ":443",
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12, // 最低 TLS 1.2
Curves: []tls.CurveID{tls.X25519, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
},
},
}
server.ListenAndServeTLS("cert.pem", "key.pem")
// HTTPS 客户端(跳过证书验证,仅测试用)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // ⚠️ 仅测试环境
},
},
}
// HTTPS 客户端(自定义 CA 证书)
caCert, _ := os.ReadFile("ca.pem")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
// mTLS(双向认证)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caCertPool,
},
},
}
TLS 版本推荐
| 版本 | 状态 | 推荐 |
|---|
| SSL 3.0 | ✗ 已废弃 | ✗ |
| TLS 1.0 | ✗ 已废弃 | ✗ |
| TLS 1.1 | ✗ 已废弃 | ✗ |
| TLS 1.2 | ✓ 仍广泛使用 | ✓ 最低要求 |
| TLS 1.3 | ✓ 最新标准 | ✓ 推荐 |
密码安全(bcrypt)
// 标准库不包含 bcrypt,需要 golang.org/x/crypto/bcrypt
import "golang.org/x/crypto/bcrypt"
// 哈希密码
hash, err := bcrypt.GenerateFromPassword([]byte("mypassword"), bcrypt.DefaultCost)
// hash: $2a$10$N9qo8uLOickgx2ZMRZoMy...
// 验证密码
err := bcrypt.CompareHashAndPassword(hash, []byte("mypassword"))
// nil = 匹配, error = 不匹配
// 修改 cost
bcrypt.GenerateFromPassword([]byte("pwd"), 12) // cost=12
| Cost | 计算时间 | 安全性 | 推荐 |
|---|
| 4 | ~1ms | 低 | 仅测试 |
| 10 | ~60ms | 中 | 默认 |
| 12 | ~250ms | 高 | 推荐 |
| 14 | ~1s | 很高 | 高安全 |
不要用 MD5/SHA256 存密码。用 bcrypt/argon2/scrypt,它们有”工作因子”防止暴力破解。
安全编码检查清单
| 检查项 | 说明 |
|---|
| ✅ 密码用 bcrypt/argon2 | 不用 MD5/SHA |
| ✅ HMAC 比较用 hmac.Equal | 不用 == |
| ✅ 对称加密用 AES-GCM | 不用 ECB/CBC |
| ✅ 随机数用 crypto/rand | 不用 math/rand |
| ✅ TLS 最低 1.2 | 不用 SSL/TLS 1.0 |
| ✅ RSA 至少 2048 位 | 或用 Ed25519 |
| ✅ 不硬编码密钥 | 用环境变量/KMS |
| ✅ SQL 用参数化查询 | 不拼接 SQL |