私钥/公钥
通过上节,我们知道了公钥(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]))
}
}

本文介绍了椭圆曲线数字签名算法ECDSA的工作原理,包括私钥和公钥的作用,签名和验证过程。ECDSA基于椭圆曲线密码学,用于确保数据的完整性和来源的可信性。签名涉及私钥的使用,而验证则使用公钥。文章以secp256k1曲线为例,展示了如何生成公私钥对,并详细阐述了签名和验证的具体步骤。
4142

被折叠的 条评论
为什么被折叠?



