Files
go-utils/password/ecdsa_ecdh.go
2025-06-14 23:28:15 +08:00

143 lines
3.7 KiB
Go

package password
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"math/big"
"strings"
)
// ECDSACrypto 实现基于 ECDSA 的加密和验证
type ECDSACrypto struct {
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
}
// NewECDSACrypto 创建一个新的 ECDSACrypto 实例
func NewECDSACrypto() (*ECDSACrypto, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return &ECDSACrypto{
privateKey: privateKey,
publicKey: &privateKey.PublicKey,
}, nil
}
// Encrypt 使用 ECDSA 对消息进行签名
func (e *ECDSACrypto) Encrypt(plainPassword string) (string, error) {
if plainPassword == "" {
return "", errors.New("密码不能为空")
}
hash := sha256.Sum256([]byte(plainPassword))
r, s, err := ecdsa.Sign(rand.Reader, e.privateKey, hash[:])
if err != nil {
return "", err
}
signature := r.String() + "$" + s.String()
return "ecdsa$" + signature, nil
}
// Verify 验证消息的签名是否有效
func (e *ECDSACrypto) Verify(plainPassword, encrypted string) (bool, error) {
if plainPassword == "" || encrypted == "" {
return false, errors.New("密码或加密字符串不能为空")
}
parts := strings.SplitN(encrypted, "$", 3)
if len(parts) != 3 || parts[0] != "ecdsa" {
return false, errors.New("加密字符串格式无效")
}
r := new(big.Int)
s := new(big.Int)
r.SetString(parts[1], 10)
s.SetString(parts[2], 10)
hash := sha256.Sum256([]byte(plainPassword))
return ecdsa.Verify(e.publicKey, hash[:], r, s), nil
}
// ECDHCrypto 实现基于 ECDH 的密钥交换
type ECDHCrypto struct {
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
}
// NewECDHCrypto 创建一个新的 ECDHCrypto 实例
func NewECDHCrypto() (*ECDHCrypto, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return &ECDHCrypto{
privateKey: privateKey,
publicKey: &privateKey.PublicKey,
}, nil
}
// Encrypt 返回公钥作为加密结果
func (e *ECDHCrypto) Encrypt(plainPassword string) (string, error) {
if plainPassword == "" {
return "", errors.New("密码不能为空")
}
publicKeyBytes := elliptic.Marshal(e.privateKey.Curve, e.publicKey.X, e.publicKey.Y)
return "ecdh$" + base64.StdEncoding.EncodeToString(publicKeyBytes), nil
}
// Verify 验证共享密钥是否一致
func (e *ECDHCrypto) Verify(plainPassword, encrypted string) (bool, error) {
if plainPassword == "" || encrypted == "" {
return false, errors.New("密码或加密字符串不能为空")
}
parts := strings.SplitN(encrypted, "$", 2)
if len(parts) != 2 || parts[0] != "ecdh" {
return false, errors.New("加密字符串格式无效")
}
publicKeyBytes, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return false, err
}
x, y := elliptic.Unmarshal(e.privateKey.Curve, publicKeyBytes)
if x == nil || y == nil {
return false, errors.New("无效的公钥")
}
sharedX, _ := e.privateKey.Curve.ScalarMult(x, y, e.privateKey.D.Bytes())
expectedHash := sha256.Sum256(sharedX.Bytes())
actualHash := sha256.Sum256([]byte(plainPassword))
return expectedHash == actualHash, nil
}
func (e *ECDHCrypto) DeriveSharedSecret(publicKey string) ([]byte, error) {
// 解码对方的公钥
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKey)
if err != nil {
return nil, err
}
// 反序列化公钥
x, y := elliptic.Unmarshal(e.privateKey.Curve, publicKeyBytes)
if x == nil || y == nil {
return nil, errors.New("无效的公钥")
}
// 计算共享密钥
sharedX, _ := e.privateKey.Curve.ScalarMult(x, y, e.privateKey.D.Bytes())
// 返回共享密钥的字节表示
return sharedX.Bytes(), nil
}