通过上节,我们知道了公钥(Q)和私钥(N)的生成的原理,我们在看看椭圆曲线数字签名算法(ECDSA)的过程,椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。ECDSA于1999年成为ANSI标准,并于2000年成为IEEE和NIST标准。
私钥主要用于签名,解密
;公钥主要用于验签,加密
,可以通过私钥可以计算出公钥,反之则不行。
公钥加密:公钥加密的内容可以用私钥来解密——只有私钥持有者才能解密。
私钥签名:私钥签名的内容可以用公钥验证。公钥能验证的签名均可视为私钥持有人所签署。
通常需要六个参数来描叙一个特定的椭圆曲线:T = (p, a, b, G, n, h).
p: 代表有限域Fp的那个质数 a,b:椭圆方程的参数 G: 椭圆曲线上的一个基点G = (xG, yG) n:G在Fp中规定的序号,一个质数。 h:余因数(cofactor),控制选取点的密度。h = #E(Fp) / n。
这里以secp256k1曲线(比特币签名所使用的曲线)为例介绍一下公私钥对的产生的过成。
secp256k1的参数为:
// randFieldElement returns a random element of the field underlying the given
// curve using the procedure given in [NSA] A.2.1.
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(rand, b)
if err != nil {
return
}
// k: 固定字节长度一个随机数
k = new(big.Int).SetBytes(b)
// 使k满足N
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}
// GenerateKey generates a public and private key pair.
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
k, err := randFieldElement(c, rand)
if err != nil {
return nil, err
}
priv := new(PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
// 公钥
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv, nil
}
func GenerateKey() (*ecdsa.PrivateKey, error) {
// secp256k1曲线
return ecdsa.GenerateKey(S256(), rand.Reader)
}
本质上ECDSA的私钥就是一个随机数满足在曲线G的n阶里及k∈(0,n),根据Q=kG可以计算出公钥,生成的私钥一般为32字节大小,公钥通常为64个字节大小。如:
私钥: 0xa8d264b13e6c7949fc31c0c7555fe10849d0f3f05af0a1ffeb8239f68b2fe7e1
公钥: 0xc22d7010aabab9ff4ee9d9468ade4fbbf8801bd90c89fd060070bc82c387cedf7730a17c9e6a2c6ef2496d54436ee957a121633bf9e9939392a8386d6682aed7
ECDSA签名算法的输入是数据的哈希值,而不是数据的本身,我们假设用户的密钥对:(d, Q);(d为私钥,Q为公钥) 待签名的信息:M; e = Hash(M);签名:Signature(e) = ( r, s)。
签名接口:
func Sign(msg []byte, seckey []byte) ([]byte, error) {
if len(msg) != 32 {
return nil, ErrInvalidMsgLen
}
if len(seckey) != 32 {
return nil, ErrInvalidKey
}
seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
return nil, ErrInvalidKey
}
var (
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
noncefunc = C.secp256k1_nonce_function_rfc6979
sigstruct C.secp256k1_ecdsa_recoverable_signature
)
if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, nil) == 0 {
return nil, ErrSignFailed
}
var (
sig = make([]byte, 65)
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
recid C.int
)
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct)
sig[64] = byte(recid) // add back recid to get 65 bytes sig
return sig, nil
}
验证接口:
func VerifySignature(pubkey, msg, signature []byte) bool {
if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 {
return false
}
sigdata := (*C.uchar)(unsafe.Pointer(&signature[0]))
msgdata := (*C.uchar)(unsafe.Pointer(&msg[0]))
keydata := (*C.uchar)(unsafe.Pointer(&pubkey[0]))
return C.secp256k1_ext_ecdsa_verify(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0
}
一个例子:
func Test_09(t *testing.T) {
priv, _ := crypto.GenerateKey()
pk := crypto.FromECDSAPub(&priv.PublicKey)
fmt.Println("priv:", hex.EncodeToString(crypto.FromECDSA(priv)))
fmt.Println("pubkey:", hex.EncodeToString(pk))
h := crypto.Keccak256([]byte{1, 2, 3, 4, 5})
s, err := crypto.Sign(h[:], priv)
if err != nil {
fmt.Println("sign err:", err)
} else {
fmt.Println("sign_data:", hex.EncodeToString(s))
fmt.Println(crypto.VerifySignature(pk, h[:], s[:64]))
}
}