以太坊的接口真香~
之前一直无法实现原因是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)
}