Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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!

🔥 总结

常量

常量大小说明
PublicKeySize32 字节公钥大小
PrivateKeySize64 字节私钥大小(种子 + 公钥)
SignatureSize64 字节签名大小
SeedSize32 字节种子大小

核心函数

函数说明返回值推荐度
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 的对比

特性Ed25519ECDSA P-256
密钥大小32 字节(公钥)64 字节(公钥)
签名大小64 字节70-72 字节(DER)
签名速度很快
验证速度
确定性✅ 是❌ 否(随机)
随机数需求❌ 不需要✅ 需要
侧信道防护✅ 常量时间⚠️ 部分实现
标准兼容性RFC 8032FIPS 186-4

crypto/ed25519 包提供了现代、高性能的 Ed25519 数字签名实现,推荐使用标准 Ed25519 变体!