golang实现ecc密钥对与[]byte类型转换

以太坊的接口真香~

之前一直无法实现原因是getKey()方法的生成密钥用的curve参数为ellipitic.P256()。

func getKey() (*ecdsa.PrivateKey, error) {
	prk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return prk, err
	}
	return prk, nil
}

  应该使用以太坊实现的S256(),基于secp256k1实现的curve。

func getKey() (*ecdsa.PrivateKey, error) {
	prk, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
	if err != nil {
		return prk, err
	}
	return prk, nil
}

FromECDSA和ToECDSA源码如下:

// ToECDSA creates a private key with the given D value.
func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
	return toECDSA(d, true)
}

// ToECDSAUnsafe blindly converts a binary blob to a private key. It should almost
// never be used unless you are sure the input is valid and want to avoid hitting
// errors due to bad origin encoding (0 prefixes cut off).
func ToECDSAUnsafe(d []byte) *ecdsa.PrivateKey {
	priv, _ := toECDSA(d, false)
	return priv
}

// toECDSA creates a private key with the given D value. The strict parameter
// controls whether the key's length should be enforced at the curve size or
// it can also accept legacy encodings (0 prefixes).
func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
	priv := new(ecdsa.PrivateKey)
	priv.PublicKey.Curve = S256()
	if strict && 8*len(d) != priv.Params().BitSize {
		return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize)
	}
	priv.D = new(big.Int).SetBytes(d)

	// The priv.D must < N
	if priv.D.Cmp(secp256k1N) >= 0 {
		return nil, fmt.Errorf("invalid private key, >=N")
	}
	// The priv.D must not be zero or negative.
	if priv.D.Sign() <= 0 {
		return nil, fmt.Errorf("invalid private key, zero or negative")
	}

	priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
	if priv.PublicKey.X == nil {
		return nil, errors.New("invalid private key")
	}
	return priv, nil
}

// FromECDSA exports a private key into a binary dump.
func FromECDSA(priv *ecdsa.PrivateKey) []byte {
	if priv == nil {
		return nil
	}
	return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}

 FromECDSAPuk和UnmarshalPubkey源码如下:

// UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
	x, y := elliptic.Unmarshal(S256(), pub)
	if x == nil {
		return nil, errInvalidPubkey
	}
	return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}

func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
	if pub == nil || pub.X == nil || pub.Y == nil {
		return nil
	}
	return elliptic.Marshal(S256(), pub.X, pub.Y)
}

密钥跟[]byte能够相互转换后,那么密钥保存就能够较为容易实现了,把[]byte封装到数据库就ok。 

下面为具体代码:

package main

import (
	"crypto/ecdsa"
	//"crypto/elliptic"
	//"crypto/rand"
	//"encoding/hex"
	"crypto/rand"
	"fmt"
	"github.com/ethereum/go-ethereum/crypto"
	"math/big"
)

func getKey() (*ecdsa.PrivateKey, error) {
	prk, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
	if err != nil {
		return prk, err
	}
	return prk, nil
}

func eccSign(data []byte, prk *ecdsa.PrivateKey) ([]byte, error) {
	r, s, err := ecdsa.Sign(rand.Reader, prk, data)
	if err != nil {
		return nil, err
	}
	params := prk.Curve.Params()
	curveOrderByteSize := params.P.BitLen() / 8
	rBytes, sBytes := r.Bytes(), s.Bytes()
	signature := make([]byte, curveOrderByteSize*2)
	copy(signature[curveOrderByteSize-len(rBytes):], rBytes)
	copy(signature[curveOrderByteSize*2-len(sBytes):], sBytes)
	return signature, nil
}

func eccVerify(data, signature []byte, puk *ecdsa.PublicKey) bool {
	curveOrderByteSize := puk.Curve.Params().P.BitLen() / 8
	r, s := new(big.Int), new(big.Int)
	r.SetBytes(signature[:curveOrderByteSize])
	s.SetBytes(signature[curveOrderByteSize:])
	return ecdsa.Verify(puk, data, r, s)
}

func main() {
	data := "00007f1f64109f1df066db39cdcfd7bb2343a02bf8a3054399f4772ed640300e"
	prk, err := getKey()
	puk := prk.PublicKey
	if err != nil {
		panic(err)
	}
	//bdata := []byte(data)
	bprk := crypto.FromECDSA(prk)
	bpuk := crypto.FromECDSAPub(&puk)

	lprk, err := crypto.ToECDSA(bprk)
	lpuk, err := crypto.UnmarshalPubkey(bpuk)

	fmt.Println(prk)
	fmt.Println(lprk)

	fmt.Println(puk)
	fmt.Println(*lpuk)
    //测试是否转换成功
	eccData, err := eccSign([]byte(data), prk)
	if err != nil {
		panic(err)
	}
	fmt.Println(eccVerify([]byte(data), eccData, lpuk))
	//  fmt.Println(data)
}

猜你喜欢

转载自blog.csdn.net/reigns_/article/details/83110066