私钥与公钥/签名与验签

私钥/公钥

通过上节,我们知道了公钥(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的参数为:

  • p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F = 2^256 − 2^32 − 2^9 − 2^8 − 2^7 − 2^6 − 2^4 − 1
  • a = 0
  • b = 7
  • G =04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
  • n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
  • h = 01
// 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)。

签名过程:

  • 1、根据ECC算法随机生成一个临时密钥对(k, R), R=(xR, yR)。(曲线上R点x,y的大整数)
  • 2、令 r = xR mod n,如果r = 0,则返回步骤1。
  • 3、计算 H = Hash(M)
  • 4、按照数据类型转换规则,将H转化为一个big endian的整数e
  • 5、s = k^-1 (e + r*d) mod n,若s = 0, 则返回步骤1 (k^-1为k对n的逆元,r为R点的x标量)
  • 6、输出的S =(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
}

验证过程:

  • 1、计算 u1 = es^-1 mod n, u2 = rs^-1 mod n
  • 2、计算 R = (xR, yR) = u1G + u2Q, 如果R = 零点,则验证该签名无效
  • 3、令 v = xR mod n
  • 4、若 v == r,则签名有效,若 v ≠ r, 则签名无效。

验证接口:

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]))
	}
}

你可能感兴趣的:(taiyuechain,区块链)