Go 语言标准库 —— crypto/ed25519 包(Ed25519 数字签名算法)
🔹 概述
crypto/ed25519 包实现了 Ed25519 数字签名算法。Ed25519 是一种现代的、高性能的数字签名方案,基于 Edwards 曲线 Curve25519。
主要功能:
- Ed25519 数字签名生成
- Ed25519 签名验证
- 支持 Ed25519ph(预哈希版本)
- 支持 Ed25519ctx(带上下文的签名)
- 常量时间实现(防侧信道攻击)
重要说明:
- ✅ Ed25519 是现代数字签名的推荐选择
- 🔒 用于 SSH、加密货币、安全协议等
- 🔑 私钥签名,公钥验证
- 📦 签名是确定性的(相同消息总是产生相同签名)
- ⚡ 性能优于 ECDSA
- 🛡️ 免疫长度扩展攻击
核心概念:
- 私钥(PrivateKey) - 64 字节(32 字节种子 + 32 字节公钥)
- 公钥(PublicKey) - 32 字节
- 签名(Signature) - 64 字节
- 种子(Seed) - 32 字节随机数据
变体:
- Ed25519 - 标准版本(默认)
- Ed25519ph - 预哈希版本(适合大消息)
- Ed25519ctx - 带上下文的版本(防止重放攻击)
🔹 常量
const (
PublicKeySize = 32 // 公钥大小(字节)
PrivateKeySize = 64 // 私钥大小(字节)
SignatureSize = 64 // 签名大小(字节)
SeedSize = 32 // 种子大小(字节)
)
- 说明:
- 所有大小都是固定的
- 无需担心密钥长度选择
- 简化了密钥管理
🔹 核心类型
PublicKey 类型
type PublicKey []byte
-
说明:
- Ed25519 公钥类型
- 固定 32 字节
-
方法:
Equal(x crypto.PublicKey) bool- 比较公钥(常量时间)
-
注意事项:
- ✅ 公钥可以公开分享
- ✅ 可直接序列化为字节
- ⚠️ 需要验证公钥的真实性
PrivateKey 类型
type PrivateKey []byte
-
说明:
- Ed25519 私钥类型
- 固定 64 字节(32 字节种子 + 32 字节公钥)
- 实现
crypto.Signer接口
-
方法:
Seed() []byte- 返回私钥种子(32 字节)Public() crypto.PublicKey- 获取公钥Equal(x crypto.PrivateKey) bool- 比较私钥(常量时间)Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error)- 签名
-
注意事项:
- ⚠️ 私钥必须严格保密
- ✅ 包含公钥,提高多次签名效率
- ✅ 可安全存储(需加密)
Options 类型
type Options struct {
Hash crypto.Hash
Context string
}
- 说明:
- 用于选择 Ed25519 变体
- 可配置哈希函数和上下文
- 字段:
Hash crypto.Hash- 哈希函数(0=Ed25519,SHA512=Ed25519ph)Context string- 上下文标识符(用于 Ed25519ctx)
🔹 核心函数
GenerateKey - 生成密钥对
ed25519.GenerateKey(random io.Reader) (PublicKey, PrivateKey, error)
-
说明:
- 生成随机的 Ed25519 密钥对
- 使用加密安全的随机数生成器
- 输出是确定性的(相同随机源产生相同密钥)
-
参数:
random io.Reader- 随机数源(可使用 nil,自动使用安全随机源)
-
返回值:
PublicKey- 公钥(32 字节)PrivateKey- 私钥(64 字节)error- 错误信息
-
示例(完整):
package main import ( "crypto/ed25519" "encoding/hex" "fmt" ) func main() { // 生成密钥对(使用默认安全随机源) publicKey, privateKey, err := ed25519.GenerateKey(nil) if err != nil { fmt.Println("错误:", err) return } // 输出密钥信息 fmt.Printf("公钥大小:%d 字节\n", len(publicKey)) fmt.Printf("私钥大小:%d 字节\n", len(privateKey)) fmt.Printf("公钥:%s\n", hex.EncodeToString(publicKey)) fmt.Printf("私钥种子:%s\n", hex.EncodeToString(privateKey.Seed())) } -
注意事项:
- ✅ Go 1.26+ 自动使用安全随机源
- ✅ 可以传入 nil 使用默认随机源
- ❌ 不要使用
math/rand
NewKeyFromSeed - 从种子生成私钥
ed25519.NewKeyFromSeed(seed []byte) PrivateKey
-
说明:
- 从种子计算私钥
- 与 RFC 8032 兼容
- 确定性生成
-
参数:
seed []byte- 种子(必须是 32 字节)
-
返回值:
PrivateKey- 私钥(64 字节)
-
示例:
package main import ( "crypto/ed25519" "crypto/rand" "encoding/hex" "fmt" "io" ) func main() { // 生成随机种子 seed := make([]byte, ed25519.SeedSize) io.ReadFull(rand.Reader, seed) // 从种子生成私钥 privateKey := ed25519.NewKeyFromSeed(seed) fmt.Printf("种子:%s\n", hex.EncodeToString(seed)) fmt.Printf("私钥:%s\n", hex.EncodeToString(privateKey)) fmt.Printf("私钥种子匹配:%v\n", string(privateKey.Seed()) == string(seed)) } -
注意事项:
- ⚠️ 种子必须是 32 字节
- ⚠️ 种子长度不正确会导致 panic
- ✅ 可重现生成相同的私钥
Sign - 签名
ed25519.Sign(privateKey PrivateKey, message []byte) []byte
-
说明:
- 对消息进行签名
- 返回 64 字节签名
- 确定性签名(相同消息总是产生相同签名)
-
参数:
privateKey PrivateKey- 私钥(64 字节)message []byte- 要签名的消息
-
返回值:
[]byte- 签名(64 字节)
-
示例(完整签名流程):
package main import ( "crypto/ed25519" "encoding/hex" "fmt" ) func main() { // 生成密钥对 publicKey, privateKey, _ := ed25519.GenerateKey(nil) // 准备消息 message := []byte("Hello, Ed25519!") // 签名 signature := ed25519.Sign(privateKey, message) fmt.Printf("消息:%s\n", string(message)) fmt.Printf("签名:%s\n", hex.EncodeToString(signature)) fmt.Printf("签名大小:%d 字节\n", len(signature)) // 验证 valid := ed25519.Verify(publicKey, message, signature) fmt.Printf("验证结果:%v\n", valid) } -
注意事项:
- ⚠️ 私钥长度不正确会导致 panic
- ✅ 不需要随机数(确定性签名)
- ✅ 签名是常量时间生成的
Verify - 验证签名
ed25519.Verify(publicKey PublicKey, message, sig []byte) bool
-
说明:
- 验证签名是否有效
- 返回布尔值表示验证结果
-
参数:
publicKey PublicKey- 公钥(32 字节)message []byte- 原始消息sig []byte- 签名(64 字节)
-
返回值:
bool- 签名是否有效
-
示例(完整验证流程):
package main import ( "crypto/ed25519" "fmt" ) func main() { // 生成密钥对 publicKey, privateKey, _ := ed25519.GenerateKey(nil) // 签名 message := []byte("Test message") signature := ed25519.Sign(privateKey, message) // 验证 valid := ed25519.Verify(publicKey, message, signature) fmt.Printf("原始验证:%v\n", valid) // 篡改消息 tamperedMessage := []byte("Tampered message") valid = ed25519.Verify(publicKey, tamperedMessage, signature) fmt.Printf("篡改后验证:%v\n", valid) // 篡改签名 tamperedSignature := make([]byte, len(signature)) copy(tamperedSignature, signature) tamperedSignature[0] ^= 0xFF // 修改第一个字节 valid = ed25519.Verify(publicKey, message, tamperedSignature) fmt.Printf("篡改签名验证:%v\n", valid) } -
注意事项:
- ⚠️ 公钥长度不正确会导致 panic
- ⚠️ 验证可能有时序攻击风险(不保护输入机密性)
- ✅ 返回 false 表示签名无效
VerifyWithOptions - 带选项的验证
ed25519.VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) error
-
说明:
- 验证签名(支持变体选项)
- 返回 error(nil 表示验证成功)
- 支持 Ed25519ph 和 Ed25519ctx
-
参数:
publicKey PublicKey- 公钥message []byte- 消息(或哈希值)sig []byte- 签名opts *Options- 选项
-
返回值:
error- nil 表示验证成功
-
示例:
package main import ( "crypto" "crypto/ed25519" "crypto/sha512" "fmt" ) func main() { publicKey, privateKey, _ := ed25519.GenerateKey(nil) // 使用 Ed25519ph(预哈希) message := []byte("Large message") hash := sha512.Sum512(message) // 签名(使用 Options) opts := &ed25519.Options{Hash: crypto.SHA512} signature, _ := privateKey.Sign(nil, hash[:], opts) // 验证 err := ed25519.VerifyWithOptions(publicKey, hash[:], signature, opts) fmt.Printf("Ed25519ph 验证:%v\n", err == nil) }
🔹 PrivateKey 方法
Seed - 获取种子
privateKey.Seed() []byte
-
说明:
- 返回私钥的种子部分(32 字节)
- 与 RFC 8032 兼容
-
返回值:
[]byte- 种子(32 字节)
-
示例:
package main import ( "crypto/ed25519" "encoding/hex" "fmt" ) func main() { publicKey, privateKey, _ := ed25519.GenerateKey(nil) // 获取种子 seed := privateKey.Seed() fmt.Printf("种子:%s\n", hex.EncodeToString(seed)) fmt.Printf("种子大小:%d 字节\n", len(seed)) // 从种子重新生成私钥 privateKey2 := ed25519.NewKeyFromSeed(seed) publicKey2 := privateKey2.Public().(ed25519.PublicKey) // 验证公钥匹配 fmt.Printf("公钥匹配:%v\n", publicKey.Equal(publicKey2)) }
Public - 获取公钥
privateKey.Public() crypto.PublicKey
-
说明:
- 返回私钥对应的公钥
- 实现
crypto.Signer接口
-
返回值:
crypto.PublicKey- 公钥(实际类型是ed25519.PublicKey)
-
示例:
package main import ( "crypto/ed25519" "fmt" ) func main() { _, privateKey, _ := ed25519.GenerateKey(nil) // 获取公钥 publicKey := privateKey.Public() fmt.Printf("公钥类型:%T\n", publicKey) fmt.Printf("公钥大小:%d 字节\n", len(publicKey.(ed25519.PublicKey))) }
Sign - 实现 crypto.Signer
privateKey.Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error)
-
说明:
- 实现
crypto.Signer接口 - 支持 Ed25519 变体
rand参数被忽略(Ed25519 是确定性的)
- 实现
-
参数:
rand io.Reader- 随机数源(被忽略)message []byte- 消息或哈希值opts crypto.SignerOpts- 选项(Hash 函数)
-
返回值:
[]byte- 签名error- 错误信息
-
示例:
package main import ( "crypto" "crypto/ed25519" "crypto/sha512" "encoding/hex" "fmt" ) func main() { _, privateKey, _ := ed25519.GenerateKey(nil) // 标准 Ed25519 签名 message := []byte("Hello") signature1, _ := privateKey.Sign(nil, message, crypto.Hash(0)) fmt.Printf("Ed25519: %s\n", hex.EncodeToString(signature1)) // Ed25519ph 签名(预哈希) hash := sha512.Sum512(message) opts := &ed25519.Options{Hash: crypto.SHA512} signature2, _ := privateKey.Sign(nil, hash[:], opts) fmt.Printf("Ed25519ph: %s\n", hex.EncodeToString(signature2)) }
🔹 完整示例
1. 基本签名和验证
package main
import (
"crypto/ed25519"
"encoding/hex"
"fmt"
)
func main() {
// 生成密钥对
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
fmt.Println("密钥生成失败:", err)
return
}
// 准备消息
message := []byte("This is a test message for Ed25519")
fmt.Printf("原始消息:%s\n", string(message))
// 签名
signature := ed25519.Sign(privateKey, message)
fmt.Printf("签名:%s\n", hex.EncodeToString(signature))
fmt.Printf("签名大小:%d 字节\n", len(signature))
// 验证
valid := ed25519.Verify(publicKey, message, signature)
fmt.Printf("验证结果:%v\n", valid)
// 验证失败场景
tamperedMessage := []byte("Tampered message")
valid = ed25519.Verify(publicKey, tamperedMessage, signature)
fmt.Printf("篡改后验证:%v\n", valid)
}
2. 密钥持久化(PEM 格式)
package main
import (
"crypto/ed25519"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"os"
)
// 保存私钥为 PEM 格式
func savePrivateKeyPEM(privateKey ed25519.PrivateKey, filename string) error {
// 转换为 DER 格式
derBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return err
}
// 编码为 PEM
pemBlock := &pem.Block{
Type: "PRIVATE KEY",
Bytes: derBytes,
}
// 写入文件
return os.WriteFile(filename, pem.EncodeToMemory(pemBlock), 0600)
}
// 加载私钥
func loadPrivateKeyPEM(filename string) (ed25519.PrivateKey, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(data)
if block == nil {
return nil, fmt.Errorf("PEM 解码失败")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return key.(ed25519.PrivateKey), nil
}
// 保存公钥为 PEM 格式
func savePublicKeyPEM(publicKey ed25519.PublicKey, filename string) error {
derBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: derBytes,
}
return os.WriteFile(filename, pem.EncodeToMemory(pemBlock), 0644)
}
// 加载公钥
func loadPublicKeyPEM(filename string) (ed25519.PublicKey, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(data)
if block == nil {
return nil, fmt.Errorf("PEM 解码失败")
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
return key.(ed25519.PublicKey), nil
}
func main() {
// 生成密钥对
publicKey, privateKey, _ := ed25519.GenerateKey(nil)
// 保存密钥
savePrivateKeyPEM(privateKey, "ed25519_private.pem")
savePublicKeyPEM(publicKey, "ed25519_public.pem")
// 加载密钥
loadedPrivate, _ := loadPrivateKeyPEM("ed25519_private.pem")
loadedPublic, _ := loadPublicKeyPEM("ed25519_public.pem")
// 验证密钥匹配
fmt.Printf("私钥匹配:%v\n", privateKey.Equal(loadedPrivate))
fmt.Printf("公钥匹配:%v\n", publicKey.Equal(loadedPublic))
// 测试签名
message := []byte("Persistent key test")
signature := ed25519.Sign(loadedPrivate, message)
valid := ed25519.Verify(loadedPublic, message, signature)
fmt.Printf("签名验证:%v\n", valid)
// 输出 PEM 内容
pemData, _ := os.ReadFile("ed25519_private.pem")
fmt.Printf("\n私钥 PEM:\n%s\n", string(pemData))
}
3. Ed25519ph(预哈希版本)
package main
import (
"crypto"
"crypto/ed25519"
"crypto/sha512"
"encoding/hex"
"fmt"
)
func main() {
publicKey, privateKey, _ := ed25519.GenerateKey(nil)
// 大消息
message := []byte("This is a very large message that we want to hash first before signing...")
// 计算 SHA-512 哈希
hash := sha512.Sum512(message)
fmt.Printf("消息哈希:%s\n", hex.EncodeToString(hash[:]))
// 使用 Ed25519ph 签名
opts := &ed25519.Options{Hash: crypto.SHA512}
signature, err := privateKey.Sign(nil, hash[:], opts)
if err != nil {
fmt.Println("签名失败:", err)
return
}
fmt.Printf("Ed25519ph 签名:%s\n", hex.EncodeToString(signature))
// 验证
err = ed25519.VerifyWithOptions(publicKey, hash[:], signature, opts)
if err != nil {
fmt.Println("验证失败:", err)
return
}
fmt.Printf("Ed25519ph 验证成功\n")
}
4. Ed25519ctx(带上下文的签名)
package main
import (
"crypto/ed25519"
"encoding/hex"
"fmt"
)
func main() {
publicKey, privateKey, _ := ed25519.GenerateKey(nil)
message := []byte("Transaction: transfer $100")
context := "my-app-v1" // 上下文标识符
// 使用上下文签名
opts := &ed25519.Options{
Hash: crypto.Hash(0),
Context: context,
}
signature, err := privateKey.Sign(nil, message, opts)
if err != nil {
fmt.Println("签名失败:", err)
return
}
fmt.Printf("消息:%s\n", string(message))
fmt.Printf("上下文:%s\n", context)
fmt.Printf("签名:%s\n", hex.EncodeToString(signature))
// 使用相同上下文验证
err = ed25519.VerifyWithOptions(publicKey, message, signature, opts)
if err != nil {
fmt.Println("验证失败:", err)
return
}
fmt.Printf("带上下文验证成功\n")
// 使用不同上下文验证(应该失败)
wrongOpts := &ed25519.Options{
Hash: crypto.Hash(0),
Context: "wrong-context",
}
err = ed25519.VerifyWithOptions(publicKey, message, signature, wrongOpts)
fmt.Printf("错误上下文验证:%v\n", err != nil)
}
5. 实际应用 - API 请求签名
package main
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type APIClient struct {
privateKey ed25519.PrivateKey
publicKey ed25519.PublicKey
clientID string
}
func NewAPIClient() (*APIClient, error) {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
return &APIClient{
privateKey: privateKey,
publicKey: publicKey,
clientID: generateClientID(publicKey),
}, nil
}
func generateClientID(publicKey ed25519.PublicKey) string {
return hex.EncodeToString(publicKey)[:16]
}
func (c *APIClient) SignRequest(method, path string, body []byte, timestamp int64) (string, error) {
// 构造签名消息
message := fmt.Sprintf("%s\n%s\n%d\n%s",
method,
path,
timestamp,
base64.StdEncoding.EncodeToString(body))
// 签名
signature := ed25519.Sign(c.privateKey, []byte(message))
return base64.StdEncoding.EncodeToString(signature), nil
}
func (c *APIClient) CreateSignedRequest(method, path string, body []byte) (*http.Request, error) {
timestamp := time.Now().Unix()
signature, err := c.SignRequest(method, path, body, timestamp)
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, "https://api.example.com"+path, strings.NewReader(string(body)))
if err != nil {
return nil, err
}
// 添加认证头
req.Header.Set("X-Client-ID", c.clientID)
req.Header.Set("X-Timestamp", fmt.Sprintf("%d", timestamp))
req.Header.Set("X-Signature", signature)
return req, nil
}
func main() {
client, _ := NewAPIClient()
fmt.Printf("客户端 ID: %s\n", client.clientID)
fmt.Printf("公钥:%s\n", hex.EncodeToString(client.publicKey))
// 创建签名请求
body := []byte(`{"action": "transfer", "amount": 100}`)
req, _ := client.CreateSignedRequest("POST", "/api/transfer", body)
fmt.Printf("\n请求头:\n")
fmt.Printf("X-Client-ID: %s\n", req.Header.Get("X-Client-ID"))
fmt.Printf("X-Timestamp: %s\n", req.Header.Get("X-Timestamp"))
fmt.Printf("X-Signature: %s\n", req.Header.Get("X-Signature"))
// 服务器端验证示例
fmt.Printf("\n=== 服务器端验证 ===\n")
clientID := req.Header.Get("X-Client-ID")
timestamp := req.Header.Get("X-Timestamp")
signature := req.Header.Get("X-Signature")
// 解析时间戳
ts, _ := strconv.ParseInt(timestamp, 10, 64)
// 验证时间戳(防止重放攻击)
if time.Now().Unix()-ts > 300 { // 5 分钟有效期
fmt.Println("错误:请求过期")
return
}
// 解码签名
sigBytes, _ := base64.StdEncoding.DecodeString(signature)
// 重构消息
bodyBytes, _ := io.ReadAll(req.Body)
message := fmt.Sprintf("%s\n%s\n%s\n%s",
req.Method,
req.URL.Path,
timestamp,
base64.StdEncoding.EncodeToString(bodyBytes))
// 验证(实际应用中需要从 clientID 查找公钥)
valid := ed25519.Verify(client.publicKey, []byte(message), sigBytes)
fmt.Printf("签名验证:%v\n", valid)
}
🔹 注意事项和最佳实践
1. 密钥管理
- ✅ 私钥 64 字节(包含公钥)
- ✅ 公钥 32 字节
- ✅ 种子 32 字节
- ✅ 使用 PEM 格式存储(PKCS#8)
// 正确 - 使用 PKCS#8 格式
derBytes, _ := x509.MarshalPKCS8PrivateKey(privateKey)
// 正确 - 从种子生成
privateKey := ed25519.NewKeyFromSeed(seed)
2. 随机数生成
- ✅ Go 1.26+ 自动使用安全随机源
- ✅ 可以传入 nil 使用默认随机源
- ❌ 不要使用
math/rand
// 正确
publicKey, privateKey, _ := ed25519.GenerateKey(nil)
// 也正确(显式指定)
publicKey, privateKey, _ := ed25519.GenerateKey(crypto/rand.Reader)
3. 签名变体选择
- ✅ Ed25519 - 标准版本(默认,适合大多数场景)
- ✅ Ed25519ph - 预哈希版本(适合大消息)
- ✅ Ed25519ctx - 带上下文(防止重放攻击)
// 标准 Ed25519
signature := ed25519.Sign(privateKey, message)
// Ed25519ph(预哈希)
hash := sha512.Sum512(message)
opts := &ed25519.Options{Hash: crypto.SHA512}
signature, _ := privateKey.Sign(nil, hash[:], opts)
// Ed25519ctx(带上下文)
opts := &ed25519.Options{Context: "my-app-v1"}
signature, _ := privateKey.Sign(nil, message, opts)
4. 确定性签名
- ✅ Ed25519 是确定性签名
- ✅ 相同消息总是产生相同签名
- ✅ 不需要随机数参数
// 两次签名结果相同
sig1 := ed25519.Sign(privateKey, message)
sig2 := ed25519.Sign(privateKey, message)
fmt.Printf("签名相同:%v\n", bytes.Equal(sig1, sig2)) // true
5. 性能优势
- ✅ 比 ECDSA 快
- ✅ 常量时间实现
- ✅ 免疫侧信道攻击
// Ed25519 性能优于 ECDSA
// 签名速度:Ed25519 > ECDSA P-256
// 验证速度:Ed25519 ≈ ECDSA P-256
// 密钥生成:Ed25519 > ECDSA P-256
6. 错误处理
- ✅ 检查所有错误
- ✅ 验证签名结果
- ⚠️ 注意 panic 情况(密钥长度不正确)
// 正确 - 检查错误
signature, err := privateKey.Sign(nil, message, opts)
if err != nil {
return nil, err
}
err = ed25519.VerifyWithOptions(publicKey, message, signature, opts)
if err != nil {
return fmt.Errorf("验证失败:%w", err)
}
// 注意 - 长度不正确会 panic
// ed25519.Sign(shortKey, message) // panic!
🔥 总结
常量
| 常量 | 大小 | 说明 |
|---|---|---|
| PublicKeySize | 32 字节 | 公钥大小 |
| PrivateKeySize | 64 字节 | 私钥大小(种子 + 公钥) |
| SignatureSize | 64 字节 | 签名大小 |
| SeedSize | 32 字节 | 种子大小 |
核心函数
| 函数 | 说明 | 返回值 | 推荐度 |
|---|---|---|---|
| GenerateKey() | 生成密钥对 | (PublicKey, PrivateKey, error) | ✅ 必需 |
| NewKeyFromSeed() | 从种子生成私钥 | PrivateKey | ✅ 确定性 |
| Sign() | 签名 | []byte | ✅ 推荐 |
| Verify() | 验证签名 | bool | ✅ 推荐 |
| VerifyWithOptions() | 带选项验证 | error | ✅ 变体使用 |
签名变体
| 变体 | 说明 | 使用场景 | 推荐度 |
|---|---|---|---|
| Ed25519 | 标准版本 | 通用场景 | ✅ 默认推荐 |
| Ed25519ph | 预哈希版本 | 大消息 | ✅ 大消息推荐 |
| Ed25519ctx | 带上下文 | 防止重放攻击 | ✅ 特定场景 |
主要特点
- 现代算法 👉 基于 Curve25519
- 高性能 👉 比 ECDSA 更快
- 确定性 👉 相同消息产生相同签名
- 常量时间 👉 防侧信道攻击
- 固定大小 👉 密钥和签名长度固定
- 免疫长度扩展 👉 安全的哈希构造
使用场景
- SSH 密钥 👉 SSH 协议支持
- 加密货币 👉 Bitcoin、Cardano 等
- TLS 证书 👉 TLS 1.3 支持
- API 认证 👉 请求签名
- 代码签名 👉 软件完整性
- 区块链 👉 分布式账本
最佳实践
- ✅ 使用默认随机源(nil 参数)
- ✅ 优先使用标准 Ed25519
- ✅ 大消息使用 Ed25519ph
- ✅ 需要上下文使用 Ed25519ctx
- ✅ PEM 格式存储密钥(PKCS#8)
- ✅ 验证公钥真实性
- ⚠️ 定期轮换密钥
- ⚠️ 防止私钥泄露
安全建议
- 🔒 使用硬件安全模块(HSM)
- 🔒 实施密钥轮换策略
- 🔒 记录签名操作日志
- 🔒 进行安全审计
- 🔒 防止时序攻击
🔹 与 ECDSA 的对比
| 特性 | Ed25519 | ECDSA P-256 |
|---|---|---|
| 密钥大小 | 32 字节(公钥) | 64 字节(公钥) |
| 签名大小 | 64 字节 | 70-72 字节(DER) |
| 签名速度 | 很快 | 快 |
| 验证速度 | 快 | 快 |
| 确定性 | ✅ 是 | ❌ 否(随机) |
| 随机数需求 | ❌ 不需要 | ✅ 需要 |
| 侧信道防护 | ✅ 常量时间 | ⚠️ 部分实现 |
| 标准兼容性 | RFC 8032 | FIPS 186-4 |
crypto/ed25519 包提供了现代、高性能的 Ed25519 数字签名实现,推荐使用标准 Ed25519 变体!