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/ecdsa 包(椭圆曲线数字签名算法)


🔹 概述

crypto/ecdsa 包实现了椭圆曲线数字签名算法(ECDSA),定义在 FIPS 186-5 标准中。

主要功能:

  • ECDSA 数字签名生成
  • ECDSA 签名验证
  • 支持 NIST P-224、P-256、P-384、P-521 曲线
  • 与 crypto/ecdh 互操作
  • 常量时间实现(防侧信道攻击)

重要说明:

  • ECDSA 是现代数字签名的标准
  • 🔒 用于证书签名、代码签名、区块链等
  • 🔑 私钥签名,公钥验证
  • 📦 签名是随机的(每次不同)
  • ⚠️ 需要先对消息进行哈希

支持的曲线:

  • P-224 - 轻量级应用
  • P-256 (secp256r1) - 常用,推荐
  • P-384 (secp384r1) - 更高安全性
  • P-521 (secp521r1) - 最高安全性

核心概念:

  • 私钥(PrivateKey) - 用于签名,必须保密
  • 公钥(PublicKey) - 用于验证签名,可以公开
  • 签名(Signature) - 由 (r, s) 组成
  • 哈希(Hash) - 消息的摘要(SHA-256 等)

🔹 核心类型

PublicKey 类型

type PublicKey struct {
    elliptic.Curve
    X, Y *big.Int
}
  • 说明:

    • 表示 ECDSA 公钥
    • 包含曲线参数和椭圆曲线上的点 (X, Y)
  • 字段:

    • Curve elliptic.Curve - 椭圆曲线
    • X *big.Int - X 坐标
    • Y *big.Int - Y 坐标
  • 方法:

    • Bytes() ([]byte, error) - 返回公钥的字节编码
    • ECDH() (*ecdh.PublicKey, error) - 转换为 ECDH 公钥
    • Equal(x crypto.PublicKey) bool - 比较公钥
  • 注意事项:

    • ✅ 公钥可以公开分享
    • ⚠️ 需要验证公钥的真实性
    • ✅ 可编码为 DER/PEM 格式

PrivateKey 类型

type PrivateKey struct {
    PublicKey
    D *big.Int
}
  • 说明:

    • 表示 ECDSA 私钥
    • 嵌入 PublicKey,包含公钥信息
  • 字段:

    • PublicKey - 嵌入的公钥
    • D *big.Int - 私钥标量(保密)
  • 方法:

    • Bytes() ([]byte, error) - 返回私钥的字节编码
    • ECDH() (*ecdh.PrivateKey, error) - 转换为 ECDH 私钥
    • Equal(x crypto.PrivateKey) bool - 比较私钥
    • Public() crypto.PublicKey - 获取公钥
    • Sign(...) ([]byte, error) - 实现 crypto.Signer 接口
  • 注意事项:

    • ⚠️ 私钥必须严格保密
    • ✅ 可安全存储(需加密)
    • ✅ 可与 X.509 证书互操作

🔹 核心函数

GenerateKey - 生成密钥对

ecdsa.GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error)

  • 说明:

    • 生成随机的 ECDSA 密钥对
    • 使用加密安全的随机数生成器
  • 参数:

    • c elliptic.Curve - 椭圆曲线(如 elliptic.P256())
    • r io.Reader - 随机数源(使用 crypto/rand.Reader)
  • 返回值:

    • *PrivateKey - 私钥
    • error - 错误信息
  • 示例(完整):

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"crypto/rand"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	// 生成 P-256 密钥对
    	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    	if err != nil {
    		fmt.Println("错误:", err)
    		return
    	}
    	
    	// 获取公钥
    	publicKey := &privateKey.PublicKey
    	
    	// 输出密钥信息
    	fmt.Printf("曲线:%s\n", privateKey.Curve.Params().Name)
    	fmt.Printf("私钥 D: %s\n", privateKey.D.Text(16))
    	fmt.Printf("公钥 X: %s\n", publicKey.X.Text(16))
    	fmt.Printf("公钥 Y: %s\n", publicKey.Y.Text(16))
    	
    	// 字节编码
    	privateBytes, _ := privateKey.Bytes()
    	publicBytes, _ := publicKey.Bytes()
    	fmt.Printf("私钥字节:%s\n", hex.EncodeToString(privateBytes))
    	fmt.Printf("公钥字节:%s\n", hex.EncodeToString(publicBytes))
    }
    
  • 注意事项:

    • ✅ 必须使用 crypto/rand.Reader
    • ❌ 不要使用 math/rand
    • ✅ Go 1.26+ 强制使用安全随机源

SignASN1 - 签名(推荐)

ecdsa.SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error)

  • 说明:

    • 对哈希值进行签名
    • 返回 ASN.1 DER 编码的签名
    • 推荐使用此函数
  • 参数:

    • r io.Reader - 随机数源(Go 1.26+ 可忽略)
    • priv *PrivateKey - 私钥
    • hash []byte - 消息的哈希值
  • 返回值:

    • []byte - ASN.1 DER 编码的签名
    • error - 错误信息
  • 示例(完整签名流程):

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"crypto/rand"
    	"crypto/sha256"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	// 生成密钥对
    	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    	
    	// 准备消息
    	message := []byte("Hello, ECDSA!")
    	
    	// 计算消息哈希
    	hash := sha256.Sum256(message)
    	
    	// 签名
    	signature, err := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
    	if err != nil {
    		fmt.Println("签名失败:", err)
    		return
    	}
    	
    	fmt.Printf("消息:%s\n", string(message))
    	fmt.Printf("哈希:%s\n", hex.EncodeToString(hash[:]))
    	fmt.Printf("签名:%s\n", hex.EncodeToString(signature))
    }
    
  • 注意事项:

    • ⚠️ 必须先对消息进行哈希
    • ✅ 签名是随机的(每次不同)
    • ✅ Go 1.26+ 自动使用安全随机源

VerifyASN1 - 验证签名(推荐)

ecdsa.VerifyASN1(pub *PublicKey, hash []byte, sig []byte) bool

  • 说明:

    • 验证 ASN.1 DER 编码的签名
    • 推荐使用此函数
  • 参数:

    • pub *PublicKey - 公钥
    • hash []byte - 消息的哈希值
    • sig []byte - ASN.1 DER 编码的签名
  • 返回值:

    • bool - 签名是否有效
  • 示例(完整验证流程):

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"crypto/rand"
    	"crypto/sha256"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	// 生成密钥对
    	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    	publicKey := &privateKey.PublicKey
    	
    	// 准备消息
    	message := []byte("Hello, ECDSA!")
    	hash := sha256.Sum256(message)
    	
    	// 签名
    	signature, _ := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
    	fmt.Printf("签名:%s\n", hex.EncodeToString(signature))
    	
    	// 验证签名
    	valid := ecdsa.VerifyASN1(publicKey, hash[:], signature)
    	fmt.Printf("签名验证:%v\n", valid)
    	
    	// 篡改消息
    	tamperedMessage := []byte("Tampered message!")
    	tamperedHash := sha256.Sum256(tamperedMessage)
    	valid = ecdsa.VerifyASN1(publicKey, tamperedHash[:], signature)
    	fmt.Printf("篡改后验证:%v\n", valid)
    }
    
  • 注意事项:

    • ⚠️ 哈希必须与签名时使用相同的哈希函数
    • ⚠️ 验证不保证时间常量(可能有时序攻击风险)
    • ✅ 返回 false 表示签名无效

Sign - 签名(返回 r, s)

ecdsa.Sign(r io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)

  • 说明:

    • 对哈希值进行签名
    • 返回签名的 r, s 分量
    • ⚠️ 大多数应用应使用 SignASN1
  • 参数:

    • r io.Reader - 随机数源
    • priv *PrivateKey - 私钥
    • hash []byte - 消息的哈希值
  • 返回值:

    • r *big.Int - 签名 r 分量
    • s *big.Int - 签名 s 分量
    • error - 错误信息
  • 示例:

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"crypto/rand"
    	"crypto/sha256"
    	"fmt"
    )
    
    func main() {
    	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    	message := []byte("Hello!")
    	hash := sha256.Sum256(message)
    	
    	// 签名(返回 r, s)
    	r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
    	if err != nil {
    		fmt.Println("签名失败:", err)
    		return
    	}
    	
    	fmt.Printf("r: %s\n", r.Text(16))
    	fmt.Printf("s: %s\n", s.Text(16))
    }
    

Verify - 验证签名(使用 r, s)

ecdsa.Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool

  • 说明:
    • 验证签名(使用 r, s 分量)
    • ⚠️ 大多数应用应使用 VerifyASN1
  • 参数:
    • pub *PublicKey - 公钥
    • hash []byte - 消息的哈希值
    • r *big.Int - 签名 r 分量
    • s *big.Int - 签名 s 分量
  • 返回值:
    • bool - 签名是否有效

ParseRawPrivateKey - 解析原始私钥

ecdsa.ParseRawPrivateKey(curve elliptic.Curve, data []byte) (*PrivateKey, error)

  • 说明:

    • 从原始字节解析私钥
    • 按照 SEC 1 v2.0 标准
  • 参数:

    • curve elliptic.Curve - 椭圆曲线
    • data []byte - 私钥字节(固定长度,大端序)
  • 返回值:

    • *PrivateKey - 私钥
    • error - 错误信息
  • 示例:

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	// 假设已有私钥字节(32 字节,P-256)
    	keyHex := "5745638975638576385763857638576385763857638576385763857638576385"
    	keyBytes, _ := hex.DecodeString(keyHex)
    	
    	// 解析私钥
    	privateKey, err := ecdsa.ParseRawPrivateKey(elliptic.P256(), keyBytes)
    	if err != nil {
    		fmt.Println("解析失败:", err)
    		return
    	}
    	
    	fmt.Printf("私钥解析成功\n")
    	fmt.Printf("D: %s\n", privateKey.D.Text(16))
    }
    

ParseUncompressedPublicKey - 解析未压缩公钥

ecdsa.ParseUncompressedPublicKey(curve elliptic.Curve, data []byte) (*PublicKey, error)

  • 说明:

    • 从未压缩格式解析公钥
    • 按照 SEC 1 v2.0 标准(X9.62 未压缩格式)
  • 参数:

    • curve elliptic.Curve - 椭圆曲线
    • data []byte - 公钥字节(0x04 前缀 + X + Y)
  • 返回值:

    • *PublicKey - 公钥
    • error - 错误信息
  • 示例:

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	// 未压缩公钥格式:0x04 + X(32 字节) + Y(32 字节)
    	pubHex := "04" +
    		"5745638975638576385763857638576385763857638576385763857638576385" +
    		"638576385763857638576385763857638576385763857638576385763857638"
    	pubBytes, _ := hex.DecodeString(pubHex)
    	
    	// 解析公钥
    	publicKey, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), pubBytes)
    	if err != nil {
    		fmt.Println("解析失败:", err)
    		return
    	}
    	
    	fmt.Printf("公钥解析成功\n")
    	fmt.Printf("X: %s\n", publicKey.X.Text(16))
    	fmt.Printf("Y: %s\n", publicKey.Y.Text(16))
    }
    

🔹 完整示例

1. 基本签名和验证

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func main() {
	// 生成密钥对
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		fmt.Println("密钥生成失败:", err)
		return
	}
	publicKey := &privateKey.PublicKey

	// 准备消息
	message := []byte("This is a test message")
	fmt.Printf("原始消息:%s\n", string(message))

	// 计算哈希
	hash := sha256.Sum256(message)
	fmt.Printf("消息哈希:%s\n", hex.EncodeToString(hash[:]))

	// 签名
	signature, err := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
	if err != nil {
		fmt.Println("签名失败:", err)
		return
	}
	fmt.Printf("签名:%s\n", hex.EncodeToString(signature))

	// 验证签名
	valid := ecdsa.VerifyASN1(publicKey, hash[:], signature)
	fmt.Printf("签名验证结果:%v\n", valid)

	// 验证失败场景(篡改消息)
	tamperedMessage := []byte("Tampered message")
	tamperedHash := sha256.Sum256(tamperedMessage)
	valid = ecdsa.VerifyASN1(publicKey, tamperedHash[:], signature)
	fmt.Printf("篡改后验证:%v\n", valid)
}

2. 使用不同曲线

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
)

func signWithCurve(curve elliptic.Curve, name string) {
	fmt.Printf("\n=== %s ===\n", name)

	// 生成密钥对
	privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		fmt.Println("错误:", err)
		return
	}

	// 准备消息
	message := []byte("Test message")
	hash := sha256.Sum256(message)

	// 签名
	signature, err := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
	if err != nil {
		fmt.Println("签名失败:", err)
		return
	}

	// 验证
	valid := ecdsa.VerifyASN1(&privateKey.PublicKey, hash[:], signature)
	
	fmt.Printf("私钥大小:%d 位\n", privateKey.D.BitLen())
	fmt.Printf("签名长度:%d 字节\n", len(signature))
	fmt.Printf("验证结果:%v\n", valid)
}

func main() {
	fmt.Println("ECDSA 不同曲线对比")
	
	signWithCurve(elliptic.P224(), "P-224")
	signWithCurve(elliptic.P256(), "P-256 (推荐)")
	signWithCurve(elliptic.P384(), "P-384")
	signWithCurve(elliptic.P521(), "P-521")
}

3. 密钥序列化与持久化

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"os"
)

// 保存私钥为 PEM 格式
func savePrivateKeyPEM(privateKey *ecdsa.PrivateKey, filename string) error {
	// 转换为 DER 格式
	derBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		return err
	}

	// 编码为 PEM
	pemBlock := &pem.Block{
		Type:  "EC PRIVATE KEY",
		Bytes: derBytes,
	}

	// 写入文件
	return os.WriteFile(filename, pem.EncodeToMemory(pemBlock), 0600)
}

// 加载私钥
func loadPrivateKeyPEM(filename string) (*ecdsa.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 解码失败")
	}

	return x509.ParseECPrivateKey(block.Bytes)
}

// 保存公钥为 PEM 格式
func savePublicKeyPEM(publicKey *ecdsa.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) (*ecdsa.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 解码失败")
	}

	pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return pubKey.(*ecdsa.PublicKey), nil
}

func main() {
	// 生成密钥对
	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	publicKey := &privateKey.PublicKey

	// 保存密钥
	savePrivateKeyPEM(privateKey, "private.pem")
	savePublicKeyPEM(publicKey, "public.pem")

	// 加载密钥
	loadedPrivate, _ := loadPrivateKeyPEM("private.pem")
	loadedPublic, _ := loadPublicKeyPEM("public.pem")

	// 验证密钥匹配
	fmt.Printf("私钥匹配:%v\n", privateKey.Equal(loadedPrivate))
	fmt.Printf("公钥匹配:%v\n", publicKey.Equal(loadedPublic))

	// 测试签名
	message := []byte("Persistent key test")
	hash := sha256.Sum256(message)
	signature, _ := ecdsa.SignASN1(rand.Reader, loadedPrivate, hash[:])
	valid := ecdsa.VerifyASN1(loadedPublic, hash[:], signature)
	fmt.Printf("签名验证:%v\n", valid)

	// 输出 PEM 内容
	pemData, _ := os.ReadFile("private.pem")
	fmt.Printf("\n私钥 PEM:\n%s\n", string(pemData))
}

4. 确定性签名(RFC 6979)

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func main() {
	// 生成密钥对
	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

	// 准备消息
	message := []byte("Deterministic signature test")
	hash := sha256.Sum256(message)

	// 普通签名(随机)
	sig1, _ := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
	sig2, _ := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
	
	fmt.Println("=== 随机签名 ===")
	fmt.Printf("签名 1: %s\n", hex.EncodeToString(sig1))
	fmt.Printf("签名 2: %s\n", hex.EncodeToString(sig2))
	fmt.Printf("签名相同:%v\n", string(sig1) == string(sig2))

	// 确定性签名(RFC 6979)
	// 使用 crypto.Signer 接口和 nil random
	opts := &crypto.SignerOpts{
		Hash: crypto.SHA256,
	}
	
	sig3, _ := privateKey.Sign(nil, hash[:], opts)
	sig4, _ := privateKey.Sign(nil, hash[:], opts)
	
	fmt.Println("\n=== 确定性签名 (RFC 6979) ===")
	fmt.Printf("签名 1: %s\n", hex.EncodeToString(sig3))
	fmt.Printf("签名 2: %s\n", hex.EncodeToString(sig4))
	fmt.Printf("签名相同:%v\n", string(sig3) == string(sig4))

	// 验证
	valid := ecdsa.VerifyASN1(&privateKey.PublicKey, hash[:], sig3)
	fmt.Printf("验证结果:%v\n", valid)
}

5. 实际应用 - 代码签名

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"time"
)

type SignedData struct {
	Data      string `json:"data"`
	Timestamp int64  `json:"timestamp"`
	Signature string `json:"signature"`
}

type CodeSigner struct {
	privateKey *ecdsa.PrivateKey
	publicKey  *ecdsa.PublicKey
}

func NewCodeSigner() (*CodeSigner, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	return &CodeSigner{
		privateKey: privateKey,
		publicKey:  &privateKey.PublicKey,
	}, nil
}

func (cs *CodeSigner) Sign(data string) (*SignedData, error) {
	// 创建带时间戳的数据
	signedData := &SignedData{
		Data:      data,
		Timestamp: time.Now().Unix(),
	}

	// 序列化
	jsonData, err := json.Marshal(signedData)
	if err != nil {
		return nil, err
	}

	// 计算哈希
	hash := sha256.Sum256(jsonData)

	// 签名
	signature, err := ecdsa.SignASN1(rand.Reader, cs.privateKey, hash[:])
	if err != nil {
		return nil, err
	}

	signedData.Signature = base64.StdEncoding.EncodeToString(signature)
	return signedData, nil
}

func (cs *CodeSigner) Verify(signedData *SignedData) (bool, error) {
	// 临时移除签名
	signature, _ := base64.StdEncoding.DecodeString(signedData.Signature)
	tempData := &SignedData{
		Data:      signedData.Data,
		Timestamp: signedData.Timestamp,
	}

	// 序列化
	jsonData, err := json.Marshal(tempData)
	if err != nil {
		return false, err
	}

	// 计算哈希
	hash := sha256.Sum256(jsonData)

	// 验证
	return ecdsa.VerifyASN1(cs.publicKey, hash[:], signature), nil
}

func main() {
	signer, _ := NewCodeSigner()

	// 签名代码
	code := "package main; func main() { println(\"Hello\") }"
	signed, _ := signer.Sign(code)

	// 输出签名数据
	output, _ := json.MarshalIndent(signed, "", "  ")
	fmt.Printf("签名数据:\n%s\n", string(output))

	// 验证签名
	valid, _ := signer.Verify(signed)
	fmt.Printf("\n签名验证:%v\n", valid)

	// 篡改数据
	signed.Data = "tampered code"
	valid, _ = signer.Verify(signed)
	fmt.Printf("篡改后验证:%v\n", valid)
}

6. 与 ECDH 互操作

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
)

func main() {
	// 生成 ECDSA 密钥对
	ecdsaKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

	// 转换为 ECDH 密钥
	ecdhPrivate, err := ecdsaKey.ECDH()
	if err != nil {
		fmt.Println("转换失败:", err)
		return
	}

	ecdhPublic, err := ecdsaKey.PublicKey.ECDH()
	if err != nil {
		fmt.Println("转换失败:", err)
		return
	}

	fmt.Printf("ECDSA 私钥已转换为 ECDH 私钥\n")
	fmt.Printf("ECDH 私钥字节长度:%d\n", len(ecdhPrivate.Bytes()))
	fmt.Printf("ECDH 公钥字节长度:%d\n", len(ecdhPublic.Bytes()))

	// 现在可以使用 ECDH 进行密钥交换
	ecdsaKey2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	ecdhKey2, _ := ecdsaKey2.ECDH()

	// ECDH 密钥交换
	shared1, _ := ecdhPrivate.ECDH(ecdhKey2.PublicKey())
	shared2, _ := ecdhKey2.ECDH(ecdhPublic)

	fmt.Printf("共享密钥 1: %x\n", shared1)
	fmt.Printf("共享密钥 2: %x\n", shared2)
	fmt.Printf("共享密钥匹配:%v\n", string(shared1) == string(shared2))
}

🔹 注意事项和最佳实践

1. 曲线选择

  • 推荐使用 P-256

    • 安全性高(128 位安全)
    • 性能好
    • 广泛支持
  • ⚠️ P-384 / P-521

    • 更高安全性
    • 性能较差
    • 特殊场景使用
// 推荐
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

// 高安全需求
privateKey, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)

2. 哈希函数选择

  • 推荐使用 SHA-256

    • 与 P-256 配合良好
    • 安全性高
    • 性能好
  • ⚠️ 避免使用 MD5 / SHA-1

    • 已不安全
    • 不应再使用
// 正确
hash := sha256.Sum256(message)

// 错误
hash := md5.Sum(message) // 不安全

3. 随机数生成

  • ✅ 必须使用 crypto/rand.Reader
  • ❌ 不要使用 math/rand
  • ✅ Go 1.26+ 强制使用安全随机源
// 正确
signature, _ := ecdsa.SignASN1(crypto/rand.Reader, privateKey, hash[:])

// 错误
signature, _ := ecdsa.SignASN1(mathRand.New(mathRand.NewSource(1)), privateKey, hash[:])

4. 签名格式

  • 使用 ASN.1 DER 编码

    • SignASN1() / VerifyASN1()
    • 标准格式
    • 互操作性好
  • ⚠️ 避免直接使用 r, s

    • 需要手动编码
    • 容易出错
// 推荐
signature, _ := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
valid := ecdsa.VerifyASN1(publicKey, hash[:], signature)

// 不推荐(除非特殊需求)
r, s, _ := ecdsa.Sign(rand.Reader, privateKey, hash[:])
valid := ecdsa.Verify(publicKey, hash[:], r, s)

5. 密钥存储

  • ✅ 私钥加密存储
  • ✅ 使用 PEM 格式(配合 x509)
  • ✅ 使用密钥管理服务(KMS)
  • ❌ 不要硬编码私钥
// 正确 - PEM 格式存储
derBytes, _ := x509.MarshalECPrivateKey(privateKey)
pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: derBytes})

// 错误 - 硬编码
privateKey := "hardcoded_key" // 不安全

6. 错误处理

  • ✅ 检查所有错误
  • ✅ 验证签名结果
  • ✅ 处理无效密钥
signature, err := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
if err != nil {
	return nil, err
}

valid := ecdsa.VerifyASN1(publicKey, hash[:], signature)
if !valid {
	return fmt.Errorf("签名验证失败")
}

🔥 总结

核心类型

类型说明用途
PublicKeyECDSA 公钥验证签名
PrivateKeyECDSA 私钥生成签名

核心函数

函数说明返回值推荐度
GenerateKey()生成密钥对(*PrivateKey, error)✅ 必需
SignASN1()签名(ASN.1)([]byte, error)✅ 强烈推荐
VerifyASN1()验证签名bool✅ 强烈推荐
Sign()签名(r, s)(r, s, error)⚠️ 特殊需求
Verify()验证(r, s)bool⚠️ 特殊需求

支持的曲线

曲线安全性性能推荐度使用场景
P-224最优⚠️ 轻量级资源受限设备
P-256✅ 强烈推荐通用场景
P-384很高⚠️ 高安全政府、金融
P-521极高⚠️ 特殊需求最高安全要求

主要特点

  • 数字签名 👉 私钥签名,公钥验证
  • 随机签名 👉 每次签名不同(更安全)
  • 常量时间 👉 防侧信道攻击
  • 标准兼容 👉 FIPS 186-5、SEC 1
  • ECDH 互操作 👉 可转换为 ECDH 密钥

使用场景

  • 证书签名 👉 X.509 证书
  • 代码签名 👉 软件完整性验证
  • 区块链 👉 比特币、以太坊等
  • JWT 签名 👉 身份认证令牌
  • 文档签名 👉 PDF、邮件签名

最佳实践

  • ✅ 优先使用 P-256 曲线
  • ✅ 使用 SHA-256 哈希
  • ✅ 使用 SignASN1/VerifyASN1
  • ✅ 使用 crypto/rand.Reader
  • ✅ 加密存储私钥
  • ✅ 验证公钥真实性
  • ⚠️ 定期轮换密钥
  • ⚠️ 防止私钥泄露

安全建议

  • 🔒 使用硬件安全模块(HSM)
  • 🔒 实施密钥轮换策略
  • 🔒 记录签名操作日志
  • 🔒 进行安全审计
  • 🔒 防止侧信道攻击

🔹 与 crypto/ecdsa 和 crypto/ecdh 的关系

特性ECDSAECDH
用途数字签名密钥交换
操作签名/验证计算共享密钥
密钥格式兼容可互相转换
典型应用证书、区块链TLS 握手、VPN

crypto/ecdsa 包提供了安全、标准的 ECDSA 数字签名实现,推荐使用 P-256 曲线和 SignASN1/VerifyASN1 函数!