ECDSA 介绍

椭圆曲线密码学简介
椭圆曲线密码学的简单介绍
ECDSA 签名验证原理及C语言实现
比特币背后的数学

基于有限域Fp的椭圆曲线域E(Fp)

椭圆曲线:

1
2
3
4
5
6
7
8
y^2 ≡ x^3 + ax + b (mod p) 
当:
a, b ∈ Fp 且满足 4a^3+27b^2 ≠ 0 (mod p),
x, y ∈ Fp时,
这条曲线上的点的集合P=(x,y)就构成了一个基于有限域Fp的椭圆曲线域E(Fp),
元素个数记作#E(Fp)。

公钥即为该曲线上的某个点Q=(x,y)的二进制输出格式。公钥可以压缩,是因为y可以根据x通过曲线函数计算出来。

椭圆曲线域E(Fp)的描述参数

1
E : y^2 ≡ x^3 + ax + b (mod p)

为描述特定的椭圆曲线域,需明确六个参数: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中的参数:

1
2
3
4
5
6
7
8
9
10
11
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
私钥的取值范围最大不能超过n。

h = 01

公钥和私钥

随机从[1,n-1]中选取一个数d, 计算Q = dG。 其中,d就是私钥,而Q即为公钥。
有限域中的加法和乘法是有特殊规定的,dG是一个标量乘法,可以转化为加法运算。
基于Fp的椭圆曲线点的集合域中,加法运算是:

1
2
3
不同的点相加: (x1, y1) ∈ E(Fp) , (x2, y2) ∈ E(Fp), x1 ≠x2 (x1, y1) + (x2, y2) = (x3, y3),其中, x3 ≡ λ^2 − x1 − x2 (mod p), y3 ≡ λ(x1 − x3) − y1 (mod p), 而λ≡ (y2 − y1)/(x2 − x1)(mod p).

相同点叠加: (x1, y1) ∈ E(Fp) , y1 ≠ 0. (x1, y1) + (x1, y1) = (x3, y3), 其中, x3 ≡ λ^2 − 2x1 (mod p), y3≡ λ(x1 − x3) − y1 (mod p), andλ≡(3x1^2+ a)/2y1(mod p).

ECDSA签名过程

用户的密钥对:(d, Q);(d为私钥,Q为公钥)
待签名的信息:M;
签名:Signature(M) = ( r, s)

签名过程:

  • 1、根据ECC算法随机生成一个密钥对(k, R), R=(xR, yR)
  • 2、令 r = xR mod n,如果r = 0,则返回步骤1
  • 3、计算 H = Hash(M)
  • 4、按照数据类型转换规则,将H转化为一个big endian的整数e
  • 5、s = k^-1 (e + rd) mod n,若s = 0, 则返回步骤1
  • 6、输出的S =(r,s)即为签名。

验证过程:

  • 1、 计算 H = Hash(M)
  • 2、按照数据类型转换规则,将H转化为一个big endian的整数e
  • 3、计算 u1 = es^-1 mod n, u2 = rs^-1 mod n
  • 4、计算 R = (xR, yR) = u1G + u2Q, 如果R = 零点,则验证该签名无效
  • 5、令 v = xR mod n
  • 6、若 v == r,则签名有效,若 v ≠ r, 则签名无效。

linux openssl tool

openssl docs -ec

生成密钥

查看支持的curves

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
openssl ecparam -list_curves
secp112r1 : SECG/WTLS curve over a 112 bit prime field
secp112r2 : SECG curve over a 112 bit prime field
secp128r1 : SECG curve over a 128 bit prime field
secp128r2 : SECG curve over a 128 bit prime field
secp160k1 : SECG curve over a 160 bit prime field
secp160r1 : SECG curve over a 160 bit prime field
secp160r2 : SECG/WTLS curve over a 160 bit prime field
secp192k1 : SECG curve over a 192 bit prime field
secp224k1 : SECG curve over a 224 bit prime field
secp224r1 : NIST/SECG curve over a 224 bit prime field
secp256k1 : SECG curve over a 256 bit prime field
secp384r1 : NIST/SECG curve over a 384 bit prime field
secp521r1 : NIST/SECG curve over a 521 bit prime field
prime192v1: NIST/X9.62/SECG curve over a 192 bit prime field
prime192v2: X9.62 curve over a 192 bit prime field
prime192v3: X9.62 curve over a 192 bit prime field
prime239v1: X9.62 curve over a 239 bit prime field
prime239v2: X9.62 curve over a 239 bit prime field
prime239v3: X9.62 curve over a 239 bit prime field
prime256v1: X9.62/SECG curve over a 256 bit prime field
sect113r1 : SECG curve over a 113 bit binary field
sect113r2 : SECG curve over a 113 bit binary field
sect131r1 : SECG/WTLS curve over a 131 bit binary field
sect131r2 : SECG curve over a 131 bit binary field
sect163k1 : NIST/SECG/WTLS curve over a 163 bit binary field
sect163r1 : SECG curve over a 163 bit binary field
sect163r2 : NIST/SECG curve over a 163 bit binary field
sect193r1 : SECG curve over a 193 bit binary field
sect193r2 : SECG curve over a 193 bit binary field
sect233k1 : NIST/SECG/WTLS curve over a 233 bit binary field
sect233r1 : NIST/SECG/WTLS curve over a 233 bit binary field
sect239k1 : SECG curve over a 239 bit binary field
sect283k1 : NIST/SECG curve over a 283 bit binary field
sect283r1 : NIST/SECG curve over a 283 bit binary field
sect409k1 : NIST/SECG curve over a 409 bit binary field
sect409r1 : NIST/SECG curve over a 409 bit binary field
sect571k1 : NIST/SECG curve over a 571 bit binary field
sect571r1 : NIST/SECG curve over a 571 bit binary field
c2pnb163v1: X9.62 curve over a 163 bit binary field
c2pnb163v2: X9.62 curve over a 163 bit binary field
c2pnb163v3: X9.62 curve over a 163 bit binary field
c2pnb176v1: X9.62 curve over a 176 bit binary field
c2tnb191v1: X9.62 curve over a 191 bit binary field
c2tnb191v2: X9.62 curve over a 191 bit binary field
c2tnb191v3: X9.62 curve over a 191 bit binary field
c2pnb208w1: X9.62 curve over a 208 bit binary field
c2tnb239v1: X9.62 curve over a 239 bit binary field
c2tnb239v2: X9.62 curve over a 239 bit binary field
c2tnb239v3: X9.62 curve over a 239 bit binary field
c2pnb272w1: X9.62 curve over a 272 bit binary field
c2pnb304w1: X9.62 curve over a 304 bit binary field
c2tnb359v1: X9.62 curve over a 359 bit binary field
c2pnb368w1: X9.62 curve over a 368 bit binary field
c2tnb431r1: X9.62 curve over a 431 bit binary field
wap-wsg-idm-ecid-wtls1: WTLS curve over a 113 bit binary field
wap-wsg-idm-ecid-wtls3: NIST/SECG/WTLS curve over a 163 bit binary field
wap-wsg-idm-ecid-wtls4: SECG curve over a 113 bit binary field
wap-wsg-idm-ecid-wtls5: X9.62 curve over a 163 bit binary field
wap-wsg-idm-ecid-wtls6: SECG/WTLS curve over a 112 bit prime field
wap-wsg-idm-ecid-wtls7: SECG/WTLS curve over a 160 bit prime field
wap-wsg-idm-ecid-wtls8: WTLS curve over a 112 bit prime field
wap-wsg-idm-ecid-wtls9: WTLS curve over a 160 bit prime field
wap-wsg-idm-ecid-wtls10: NIST/SECG/WTLS curve over a 233 bit binary field
wap-wsg-idm-ecid-wtls11: NIST/SECG/WTLS curve over a 233 bit binary field
wap-wsg-idm-ecid-wtls12: WTLS curve over a 224 bit prime field
Oakley-EC2N-3:
IPSec/IKE/Oakley curve #3 over a 155 bit binary field.
Not suitable for ECDSA.
Questionable extension field!
Oakley-EC2N-4:
IPSec/IKE/Oakley curve #4 over a 185 bit binary field.
Not suitable for ECDSA.
Questionable extension field!
brainpoolP160r1: RFC 5639 curve over a 160 bit prime field
brainpoolP160t1: RFC 5639 curve over a 160 bit prime field
brainpoolP192r1: RFC 5639 curve over a 192 bit prime field
brainpoolP192t1: RFC 5639 curve over a 192 bit prime field
brainpoolP224r1: RFC 5639 curve over a 224 bit prime field
brainpoolP224t1: RFC 5639 curve over a 224 bit prime field
brainpoolP256r1: RFC 5639 curve over a 256 bit prime field
brainpoolP256t1: RFC 5639 curve over a 256 bit prime field
brainpoolP320r1: RFC 5639 curve over a 320 bit prime field
brainpoolP320t1: RFC 5639 curve over a 320 bit prime field
brainpoolP384r1: RFC 5639 curve over a 384 bit prime field
brainpoolP384t1: RFC 5639 curve over a 384 bit prime field
brainpoolP512r1: RFC 5639 curve over a 512 bit prime field
brainpoolP512t1: RFC 5639 curve over a 512 bit prime field

选择一种curve生成private key:

1
openssl ecparam -name secp384r1 -genkey -out ecdsa_p384_sign.key

生成密钥文件的pem内容:

1
2
3
4
5
6
-----BEGIN EC PARAMETERS-----
context
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
context
-----END EC PRIVATE KEY-----

查看私钥内容:

1
2
3
4
5
6
7
8
9
10
openssl ec -in ecdsa_p384_sign.key -text -noout

read EC key
Private-Key: (384 bit)
priv:
content
pub:
content
ASN1 OID: secp384r1
NIST CURVE: P-384

根据私钥生成公钥:

1
openssl ec -in ecdsa_p384_sign.key -pubout -out ecdsa_p384_verify.key

生成证书请求

1
2
openssl req -new -key ecdsa_p384_sign.key -out ecdsa_p384_sign.csr
然后输入一系列参数信息

生成自签名证书

1
2
openssl req -new -key ecdsa_p384_sign.key -x509 -nodes -days 365 -out ecdsa_p384_cert.pem
然后输入一系列参数信息

Python ECDSA

依赖

1
ecdsa

ecdsa

简单使用

1
2
3
4
5
from ecdsa import SigningKey, NIST384p
sk = SigningKey.generate(curve=NIST384p)
vk = sk.get_verifying_key()
signature = sk.sign("message")
assert vk.verify(signature, "message")

支持的curve

ecdsa支持NIST Curve P-192、NIST Curve P-224、NIST Curve P-256、NIST Curve P-384、NIST Curve P-521和Certicom secp256-k1
默认的hash为sha1,也可以自定义其他hashlib有的hashfunc,但是需要注意的是hashfunc输出的hash长度不要超过curve生成的签名长度。

1
2
3
4
5
6
7
8
hashfunc= should behave like hashlib.sha1 . The output length of the
hash (in bytes) must not be longer than the length of the curve order
(rounded up to the nearest byte), so using SHA256 with nist256p is
ok, but SHA256 with nist192p is not. (In the 2**-96ish unlikely event
of a hash output larger than the curve order, the hash will
effectively be wrapped mod n).
Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode,
or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256.

curve定义的参数在python-ecdsa/src/ecdsa/ecdsa.py文件里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# NIST Curve P-192:
_p = 6277101735386680763835789423207666416083908700390324961279
_r = 6277101735386680763835789423176059013767194773182842284081
# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L
# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L
_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1
_Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012
_Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811

curve_192 = ellipticcurve.CurveFp(_p, -3, _b)
generator_192 = ellipticcurve.Point(curve_192, _Gx, _Gy, _r)


# NIST Curve P-224:
_p = 26959946667150639794667015087019630673557916260026308143510066298881
_r = 26959946667150639794667015087019625940457807714424391721682722368061
# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L
# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL
_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4
_Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21
_Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34

curve_224 = ellipticcurve.CurveFp(_p, -3, _b)
generator_224 = ellipticcurve.Point(curve_224, _Gx, _Gy, _r)

# NIST Curve P-256:
_p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
_r = 115792089210356248762697446949407573529996955224135760342422259061068512044369
# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L
# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL
_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
_Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
_Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5

curve_256 = ellipticcurve.CurveFp(_p, -3, _b)
generator_256 = ellipticcurve.Point(curve_256, _Gx, _Gy, _r)

# NIST Curve P-384:
_p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319
_r = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643
# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L
# c = 0x79d1e655f868f02fff48dcdee14151ddb80643c1406d0ca10dfe6fc52009540a495e8042ea5f744f6e184667cc722483L
_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef
_Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7
_Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f

curve_384 = ellipticcurve.CurveFp(_p, -3, _b)
generator_384 = ellipticcurve.Point(curve_384, _Gx, _Gy, _r)

# NIST Curve P-521:
_p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151
_r = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449
# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL
# c = 0x0b48bfa5f420a34949539d2bdfc264eeeeb077688e44fbf0ad8f6d0edb37bd6b533281000518e19f1b9ffbe0fe9ed8a3c2200b8f875e523868c70c1e5bf55bad637L
_b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
_Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66
_Gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650

curve_521 = ellipticcurve.CurveFp(_p, -3, _b)
generator_521 = ellipticcurve.Point(curve_521, _Gx, _Gy, _r)

# Certicom secp256-k1
_a = 0x0000000000000000000000000000000000000000000000000000000000000000
_b = 0x0000000000000000000000000000000000000000000000000000000000000007
_p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
_Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
_r = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b)
generator_secp256k1 = ellipticcurve.Point(curve_secp256k1, _Gx, _Gy, _r)

私钥操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

from ecdsa import SigningKey, SECP256k1, NIST384p, NIST521p
import hashlib
import os

class ECDSA_Private_Key(object):

"""docstring for ECDSA_Private_Key"""

def __init__(self, key_filename):
super(ECDSA_Private_Key, self).__init__()
self.key_filename = key_filename
with open(self.key_filename,"rb") as f:
self.private_key = SigningKey.from_pem(f.read())
self.privkey = self.private_key.privkey

def print_key_message(self):
print("ECDSA Private Key Info:")
print("ECDSA private key baselen: " + str(self.private_key.baselen*8) + " bits")
print("ECDSA curve name: " + self.private_key.curve.name)
print("ECDSA curve verifying key length: " + str(self.private_key.curve.verifying_key_length))
print("ECDSA curve signature length: " + str(self.private_key.curve.signature_length))
print("ECDSA curve oid:")
print(str(self.private_key.curve.oid))

def print_key_message_to_file(self,filename = None):
f_name = ''
if filename != None:
f_name = filename
else:
f_name = os.path.splitext(self.key_filename)[0] + '.txt'
with open(f_name,"w") as f:
f.write(self.key_filename + '\r\n')
f.write("ECDSA private key baselen: " + str(self.private_key.baselen*8) + " bits\n")
f.write("ECDSA curve name: " + self.private_key.curve.name + "\n")
f.write("ECDSA curve verifying key length: " + str(self.private_key.curve.verifying_key_length) + "\n")
f.write("ECDSA curve signature length: " + str(self.private_key.curve.signature_length) + "\n")
f.write("ECDSA curve oid:\n")
f.write(str(self.private_key.curve.oid))

def sign(self,message,hash_alg=1):
if hash_alg == 1:
return self.private_key.sign(message,hashfunc=hashlib.sha256)
elif hash_alg == 2:
if self.private_key.baselen*8 == 512:
return self.private_key.sign(message,hashfunc=hashlib.sha512)
else:
raise RuntimeError('hash length longer than the ecdsa length')

def main():
ecdsa_private_key = ECDSA_Private_Key("ecdsa_p384_sign.key")
ecdsa_private_key.print_key_message()
ecdsa_private_key.print_key_message_to_file()

if __name__ == '__main__':
main()

公钥操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

from ecdsa import VerifyingKey, SECP256k1, NIST384p, NIST521p
import hashlib
import os

class ECDSA_Public_Key(object):

"""docstring for ECDSA_Public_Key"""

def __init__(self, key_filename = None, key_point = None, verify_alg = None, hash_alg = None):
super(ECDSA_Public_Key, self).__init__()
if key_filename != None:
self.key_filename = key_filename
with open(self.key_filename,"rb") as f:
self.public_key = VerifyingKey.from_pem(f.read())
self.pubkey = self.public_key.pubkey
elif key_point != None and verify_alg != None and hash_alg != None:
key_hashfunc = hashlib.sha256
key_curve = SECP256k1
if hash_alg == 1:
key_hashfunc = hashlib.sha256
elif hash_alg == 2:
key_hashfunc = hashlib.sha512
if verify_alg == 0x10:
key_curve = SECP256k1
elif verify_alg == 0x11:
key_curve = NIST384p
elif verify_alg == 0x12:
key_curve = NIST521p
self.public_key = VerifyingKey.from_public_point(key_point, curve=key_curve, hashfunc=key_hashfunc)
self.pubkey = self.public_key.pubkey

def print_key_message(self):
print("ECDSA Public Key Info:")
print("ECDSA public key point: ")
print(str(self.pubkey.point))
print("ECDSA curve name: " + self.public_key.curve.name)
print("ECDSA curve verifying key length: " + str(self.public_key.curve.verifying_key_length))
print("ECDSA curve signature length: " + str(self.public_key.curve.signature_length))
print("ECDSA curve oid:")
print(str(self.public_key.curve.oid))

def print_key_message_to_file(self,filename = None):
f_name = ''
if filename != None:
f_name = filename
else:
f_name = os.path.splitext(self.key_filename)[0] + '.txt'
with open(f_name,"w") as f:
f.write(self.key_filename + '\n')
f.write("ECDSA Public Key Info: \n")
f.write(str(self.pubkey.point))
f.write("\n")
f.write("ECDSA curve name: " + self.public_key.curve.name + "\n")
f.write("ECDSA curve verifying key length: " + str(self.public_key.curve.verifying_key_length) + "\n")
f.write("ECDSA curve signature length: " + str(self.public_key.curve.signature_length) + "\n")
f.write("ECDSA curve oid:\n")
f.write(str(self.public_key.curve.oid))

def verify(self,message,signature,hash_alg):
if hash_alg == 1:
return self.public_key.verify(signature, message, hashfunc=hashlib.sha256)
elif hash_alg == 2:
return self.public_key.verify(signature, message, hashfunc=hashlib.sha512)

def main():
ecdsa_public_key = ECDSA_Public_Key("ecdsa_p384_verify.key")
ecdsa_public_key.print_key_message()
ecdsa_public_key.print_key_message_to_file()

if __name__ == '__main__':
main()

ECDSA test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

from ecdsa_private_key_handle import ECDSA_Private_Key
from ecdsa_public_key_handle import ECDSA_Public_Key

def main():
message = "hello mrbluyee."
ecdsa_private_key = ECDSA_Private_Key("ecdsa_p384_sign.key")
ecdsa_public_key = ECDSA_Public_Key("ecdsa_p384_verify.key")
ecdsa_public_key.print_key_message()
print("\n")
ecdsa_private_key.print_key_message()
print("\n")

message_sign = ecdsa_private_key.sign(message.encode(), 1)
print("message_sign:")
print(len(message_sign))
print([hex(i) for i in message_sign])

message_verify0 = ecdsa_public_key.verify(message.encode(), message_sign, 1)
print("message verify0:")
print(message_verify0)

if __name__ == '__main__':
main()

运行结果显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ECDSA Public Key Info:
ECDSA public key point:
(x,y)
ECDSA curve name: NIST384p
ECDSA curve verifying key length: 96
ECDSA curve signature length: 96
ECDSA curve oid:
(1, 3, 132, 0, 34)


ECDSA Private Key Info:
ECDSA private key baselen: 384 bits
ECDSA curve name: NIST384p
ECDSA curve verifying key length: 96
ECDSA curve signature length: 96
ECDSA curve oid:
(1, 3, 132, 0, 34)


message_sign:
96
['0xa2', '0x9c', '0xc9', '0x5', '0x1a', '0x96', '0x33', '0x83', '0xe9', '0x79', '0xb4', '0x2b', '0x66', '0xa7', '0xf7', '0xc3', '0x4e', '0x4d', '0x87', '0xd6', '0xb5', '0xe', '0xc0', '0xe5', '0x6a', '0xaa', '0x10', '0x55', '0xd9', '0x58', '0x11', '0x17', '0x97', '0xc6', '0x30', '0x95', '0xc9', '0x1f', '0x73', '0xcc', '0x1f', '0x16', '0xf7', '0xd0', '0x5a', '0x92', '0x2f', '0x4d', '0x26', '0x99', '0xc5', '0x5', '0xda', '0x59', '0x1f', '0x69', '0x37', '0x86', '0x4e', '0x3a', '0xe0', '0x9f', '0x8b', '0xd2', '0x85', '0x1e', '0xbe', '0xfa', '0x3f', '0x71', '0x37', '0x4e', '0x37', '0xcc', '0xb', '0xc7', '0x12', '0x5', '0x62', '0x81', '0x88', '0x56', '0x65', '0x87', '0x2d', '0xb4', '0xe1', '0xf8', '0x44', '0xd2', '0xe6', '0x3f', '0x5b', '0xcd', '0x14', '0x13']
message verify0:
True

Python OpenSSL

依赖

1
OpenSSL

私钥操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

import OpenSSL
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
import os

class ECDSA_PRIVATE_KEY(object):

"""docstring for ECDSA_PRIVATE_KEY"""

def __init__(self, key_filename, passwd = None):
super(ECDSA_PRIVATE_KEY, self).__init__()
self.key_filename = key_filename
if passwd != None:
with open(self.key_filename,"rb") as f:
self.private_key = serialization.load_pem_private_key( \
f.read(), \
password = passwd.encode(), \
backend = default_backend() \
)
else:
with open(self.key_filename,"rb") as f:
self.private_key = serialization.load_pem_private_key( \
f.read(), \
password = None, \
backend = default_backend() \
)
self.private_key_numbers = self.private_key.private_numbers()
self.private_value = self.private_key_numbers.private_value
self.private_key_numbers_public_numbers = self.private_key_numbers.public_numbers
self.curve = self.private_key_numbers_public_numbers.curve
self.point_x = self.private_key_numbers_public_numbers.x
self.point_y = self.private_key_numbers_public_numbers.y
self.encode_point = self.private_key_numbers_public_numbers.encode_point()

def print_key_message(self):
print("ECDSA Private Key Info:")
print("key size: " + str(self.private_key.key_size) + " bits")
print("private value: " + str(self.private_value))
print("curve: " + str(self.curve.name))
print("point x: " + str(self.point_x))
print("point y: " + str(self.point_y))
print("encode point length: " + str(len(self.encode_point)))
print("encode point: ")
print(','.join([hex(i) for i in self.encode_point]))


def print_key_message_to_file(self,filename = None):
f_name = ''
if filename != None:
f_name = filename
else:
f_name = os.path.splitext(self.key_filename)[0] + '.txt'
with open(f_name,"w") as f:
f.write(self.key_filename + "\n")
f.write("ECDSA Private Key Info:\n")
f.write("key size: " + str(self.private_key.key_size) + " bits\r\n")
f.write("private value: " + str(self.private_value) + "\r\n")
f.write("curve: " + str(self.curve.name) + "\r\n")
f.write("point x: " + str(self.point_x) + "\r\n")
f.write("point y: " + str(self.point_y) + "\r\n")
f.write("encode point length: " + str(len(self.encode_point)) + "\n")
f.write("encode point: \n")
f.write(','.join([hex(i) for i in self.encode_point]))
f.write("\n")

def sign(self,message,hash_alg):
if hash_alg == 1:
return self.private_key.sign( \
message, \
ec.ECDSA(hashes.SHA256()) \
)
elif hash_alg == 2:
return self.private_key.sign( \
message, \
ec.ECDSA(hashes.SHA512()) \
)

def main():
ecdsa_private_key = ECDSA_PRIVATE_KEY("ecdsa_p384_sign.key")
ecdsa_private_key.print_key_message()
ecdsa_private_key.print_key_message_to_file()

if __name__ == '__main__':
main()

公钥操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

import OpenSSL
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding,rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends.interfaces import EllipticCurveBackend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicNumbers
from ecdsa_key_content_len import ECDSA_KEY_LEN
import os

class ECDSA_PUBLIC_KEY(object):

"""docstring for ECDSA_PUBLIC_KEY"""

def __init__(self, key_filename = None, key_pem_bytes = None, key_curve = None, key_encode_point = None):
super(ECDSA_PUBLIC_KEY, self).__init__()
if key_filename != None:
self.key_filename = key_filename
with open(self.key_filename,"rb") as f:
self.public_key = serialization.load_pem_public_key( \
f.read(), \
backend = default_backend() \
)
self.key_len_info = ECDSA_KEY_LEN.ECDSA_KEY[self.public_key.key_size]
self.public_key_numbers = self.public_key.public_numbers()
self.curve = self.public_key_numbers.curve
self.point_x = self.public_key_numbers.x
self.point_y = self.public_key_numbers.y
self.point_x_bytes = self.point_x.to_bytes(self.key_len_info["x_len"], byteorder= 'big')
self.point_y_bytes = self.point_y.to_bytes(self.key_len_info["y_len"], byteorder= 'big')
self.encode_point = self.public_key_numbers.encode_point()
elif key_pem_bytes != None:
self.public_key = serialization.load_pem_public_key( \
key_pem_bytes, \
backend = default_backend() \
)
self.key_len_info = ECDSA_KEY_LEN.ECDSA_KEY[self.public_key.key_size]
self.public_key_numbers = self.public_key.public_numbers()
self.curve = self.public_key_numbers.curve
self.point_x = self.public_key_numbers.x
self.point_y = self.public_key_numbers.y
self.point_x_bytes = self.point_x.to_bytes(self.key_len_info["x_len"], byteorder= 'big')
self.point_y_bytes = self.point_y.to_bytes(self.key_len_info["y_len"], byteorder= 'big')
self.encode_point = self.public_key_numbers.encode_point()
elif key_curve != None and key_encode_point != None:
self.public_key_numbers = EllipticCurvePublicNumbers.from_encoded_point(key_curve, key_encode_point)
self.curve = self.public_key_numbers.curve
self.point_x = self.public_key_numbers.x
self.point_y = self.public_key_numbers.y
self.encode_point = self.public_key_numbers.encode_point()
backend = default_backend()
self.public_key = backend.load_elliptic_curve_public_numbers(self.public_key_numbers)
self.key_len_info = ECDSA_KEY_LEN.ECDSA_KEY[self.public_key.key_size]
self.point_x_bytes = self.point_x.to_bytes(self.key_len_info["x_len"], byteorder= 'big')
self.point_y_bytes = self.point_y.to_bytes(self.key_len_info["y_len"], byteorder= 'big')

def print_key_message(self):
print("ECDSA Private Key Info:")
print("key size: " + str(self.public_key.key_size) + " bits")
print("curve: " + str(self.curve.name))
print("point x: " + str(self.point_x))
print("point x bytes: ")
print(','.join([hex(i) for i in self.point_x_bytes]))
print("point y: " + str(self.point_y))
print("point y bytes: ")
print(','.join([hex(i) for i in self.point_y_bytes]))
print("encode point length: " + str(len(self.encode_point)))
print("encode point: ")
print(','.join([hex(i) for i in self.encode_point]))

def print_key_message_to_file(self,filename = None):
f_name = ''
if filename != None:
f_name = filename
else:
f_name = os.path.splitext(self.key_filename)[0] + '.txt'
with open(f_name,"w") as f:
f.write(self.key_filename + "\n")
f.write("ECDSA Public Key Info:\n")
f.write("key size: " + str(self.public_key.key_size) + " bits\r\n")
f.write("curve: " + str(self.curve.name) + "\r\n")
f.write("point x: " + str(self.point_x) + "\r\n")
f.write("point x bytes: \n")
f.write(','.join([hex(i) for i in self.point_x_bytes]))
f.write("\r\n")
f.write("point y: " + str(self.point_y) + "\r\n")
f.write("point y bytes: \n")
f.write(','.join([hex(i) for i in self.point_y_bytes]))
f.write("\r\n")
f.write("encode point length: " + str(len(self.encode_point)) + "\n")
f.write("encode point: \n")
f.write(','.join([hex(i) for i in self.encode_point]))
f.write("\n")

def verify(self,message,signature,hash_alg):
if hash_alg == 1:
return self.public_key.verify( \
signature, \
message, \
ec.ECDSA(hashes.SHA256()) \
)
elif hash_alg == 2:
return self.public_key.verify( \
signature, \
message, \
ec.ECDSA(hashes.SHA512()) \
)

def main():
ecdsa_public_key = ECDSA_PUBLIC_KEY(key_filename = "ecdsa_p384_verify.key")
ecdsa_public_key.print_key_message()
ecdsa_public_key.print_key_message_to_file()

if __name__ == '__main__':
main()

key_len_file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

class ECDSA_KEY_LEN(object):

"""docstring for ECDSA_KEY_LEN"""

ECDSA_P256 = {
"x_len" : 32,
"y_len" : 32
}

ECDSA_P384 = {
"x_len" : 48,
"y_len" : 48
}

ECDSA_P521 = {
"x_len" : 66,
"y_len" : 66
}

ECDSA_KEY = {
256 : ECDSA_P256,
384 : ECDSA_P384,
521 : ECDSA_P521
}

def __init__(self):
super(ECDSA_KEY_LEN, self).__init__()

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: UTF-8 -*-

__author__ = 'Mr.Bluyee'

from ecdsa_private_key_handle import ECDSA_PRIVATE_KEY
from ecdsa_public_key_handle import ECDSA_PUBLIC_KEY

def main():
message = "hello mrbluyee."
ecdsa_private_key = ECDSA_PRIVATE_KEY("ecdsa_p384_sign.key")
ecdsa_public_key = ECDSA_PUBLIC_KEY(key_filename = "ecdsa_p384_verify.key")

message_sign = ecdsa_private_key.sign(message.encode(),1)
print("message_sign:")
print(len(message_sign))
print([hex(i) for i in message_sign])

message_verify = ecdsa_public_key.verify(message.encode(),message_sign,1)
print("message verify:")
print(message_verify)

if __name__ == '__main__':
main()

运行输出:

1
2
3
4
5
message_sign:
102
['0x30', '0x64', '0x2', '0x30', '0x62', '0xe6', '0xb5', '0xd', '0x29', '0x63', '0x3', '0x34', '0x81', '0x9b', '0x68', '0xd', '0x39', '0xa8', '0xd0', '0x35', '0xd8', '0xe5', '0x77', '0xe5', '0xa4', '0x69', '0xdb', '0x4', '0x22', '0x57', '0x76', '0x7', '0x29', '0xff', '0xa4', '0xda', '0xc9', '0x44', '0x1f', '0x95', '0x6b', '0xe6', '0x8b', '0x87', '0x42', '0x8f', '0xce', '0x57', '0x6c', '0x49', '0xd1', '0x43', '0x2', '0x30', '0x5e', '0x9a', '0xc8', '0xe', '0xdb', '0x72', '0xb4', '0xee', '0xf7', '0xb5', '0xb', '0x6f', '0xeb', '0xd4', '0x2c', '0x3b', '0x85', '0xcb', '0xc2', '0x79', '0x5a', '0x6', '0x6d', '0x79', '0x70', '0x1', '0xdc', '0x2a', '0x35', '0x62', '0x3a', '0x8c', '0x5c', '0xa5', '0xd2', '0x57', '0xb', '0xed', '0x88', '0xe0', '0x56', '0xba', '0xb1', '0x2f', '0xc9', '0x8f', '0x20', '0xa7']
message verify:
None

文章部分内容整理自:
比特币系统采用的公钥密码学方案和ECDSA签名算法介绍——第一部分:原理