1
2
3
|
import six import timeit #查找任何特定代码执行的确切时间 from ecdsa.curves import curves |
1
2
3
4
5
6
7
8
9
10
11
|
#定义do函数,计算时间 def do(setup_statements, statement): # extracted from timeit.py t = timeit.Timer(stmt = statement, setup = "\n" .join(setup_statements)) # determine number so that 0.2 <= total time < 2.0 for i in range ( 1 , 10 ): number = 10 * * i #**为次方 x = t.timeit(number) if x > = 0.2 : break return x / number |
NIST为数字测试套件关于NIST详解
GF§ (素数域)曲线,密钥长度为192、224、256、384和521bit
OpenSSL工具(openssl ecparam -list_curves
)所知道的这些曲线的 "简称 "是:prime192v1
、secp224r1
、prime256v1
、secp384r1
和secp521r1
。它包括比特币使用的256位曲线secp256k1。它还支持160到512位的Brainpool曲线的常规(非扭曲)变体。这些曲线的 "简称 "是:BrainpoolP160r1, brainpoolP192r1, brainpoolP224r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1, brainpoolP512r1。少数来自SEC标准的小曲线也包括在内(主要是为了加快库的测试),它们是:secp112r1, secp112r2, secp128r1, 和secp160r1。没有包括其他的曲线,但要增加对更多素数域的曲线的支持并不难。
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
|
#不是很懂 sep=":",unit="s",form=".5f",form_inv=".2f", prnt_form = ( "{name:>16}{sep:1} {siglen:>6} {keygen:>9{form}}{unit:1} " "{keygen_inv:>9{form_inv}} {sign:>9{form}}{unit:1} " "{sign_inv:>9{form_inv}} {verify:>9{form}}{unit:1} " "{verify_inv:>9{form_inv}} {verify_single:>13{form}}{unit:1} " "{verify_single_inv:>14{form_inv}}" ) print ( prnt_form. format ( siglen = "siglen" , keygen = "keygen" , keygen_inv = "keygen/s" , sign = "sign" , sign_inv = "sign/s" , verify = "verify" , verify_inv = "verify/s" , verify_single = "no PC verify" , verify_single_inv = "no PC verify/s" , name = "", sep = "", unit = "", form = "", form_inv = "", ) ) for curve in [i.name for i in curves]: S1 = "import six; from ecdsa import SigningKey, %s" % curve S2 = "sk = SigningKey.generate(%s)" % curve #产生私钥 S3 = "msg = six.b('msg')" #消息 S4 = "sig = sk.sign(msg)" #签名 S5 = "vk = sk.get_verifying_key()" #公钥由私钥得出 get_verifying_key()函数 S6 = "vk.precompute()" #不懂 S7 = "vk.verify(sig, msg)" #用公钥验证签名 # 我们碰巧知道.generate()也在计算验证密钥,这是最耗时的部分。如果将代码改为懒惰地计算vk,我们就需要将这个基准改为在S5上循环,而不是在S2上。 keygen = do([S1], S2) sign = do([S1, S2, S3], S4) verf = do([S1, S2, S3, S4, S5, S6], S7) verf_single = do([S1, S2, S3, S4, S5], S7) import ecdsa c = getattr (ecdsa, curve) #从名字上看获取属性值 sig = ecdsa.SigningKey.generate(c).sign(six.b( "msg" )) #密钥对(keygen)、签署数据(sign)、验证这些签名(verify)、共享秘密(ecdh)以及在没有特定密钥预计算的情况下验证签名(no PC verify)、原始签名的大小(通常是签名可以被编码的最小方式)也在siglen栏中提供 print ( prnt_form. format ( name = curve, #所有的曲线 sep = ":" , siglen = len (sig), unit = "s" , keygen = keygen, keygen_inv = 1.0 / keygen, sign = sign, sign_inv = 1.0 / sign, verify = verf, verify_inv = 1.0 / verf, verify_single = verf_single, verify_single_inv = 1.0 / verf_single, form = ".5f" , #小数点后面为5位 form_inv = ".2f" , #小数点后面为2位 ) ) |
1
|
print ("") |
ED25519和Cureve5519
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
|
ecdh_form = "{name:>16}{sep:1} {ecdh:>9{form}}{unit:1} {ecdh_inv:>9{form_inv}}" print ( ecdh_form. format ( ecdh = "ecdh" , ecdh_inv = "ecdh/s" , name = "", sep = "", unit = "", form = "", form_inv = "", ) ) for curve in [i.name for i in curves]: if curve = = "Ed25519" or curve = = "Ed448" : continue S1 = "from ecdsa import SigningKey, ECDH, {0}" . format (curve) S2 = "our = SigningKey.generate({0})" . format (curve) #私钥 S3 = "remote = SigningKey.generate({0}).verifying_key" . format (curve) #公钥 S4 = "ecdh = ECDH(private_key=our, public_key=remote)" S5 = "ecdh.generate_sharedsecret_bytes()" #产生共享密钥 ecdh = do([S1, S2, S3, S4], S5) print ( ecdh_form. format ( name = curve, sep = ":" , unit = "s" , form = ".5f" , form_inv = ".2f" , ecdh = ecdh, ecdh_inv = 1.0 / ecdh, ) ) |
1
2
3
4
5
|
from ecdsa import SigningKey sk = SigningKey.generate() # uses NIST192p生成私钥 vk = sk.verifying_key #在私钥的基础上生成公钥 signature = sk.sign(b "message" ) #用私钥对消息进行签名 assert vk.verify(signature, b "message" ) #用公钥去验证。assert为一断言函数:不满足条件直接触发异常忙不执行接下来的代码,括号中为condition |
1
2
3
4
5
|
from ecdsa import SigningKey, NIST384p #384位NIST素域椭圆曲线,其中私钥/公钥都与特定的曲线相关联,更长的曲线更安全,但时间长,密钥和签名也长 sk = SigningKey.generate(curve = NIST384p) vk = sk.verifying_key signature = sk.sign(b "message" ) assert vk.verify(signature, b "message" ) |
1
2
3
4
5
6
7
|
#将签名密钥(私钥)序列化成不同的格式。 from ecdsa import SigningKey, NIST384p sk = SigningKey.generate(curve = NIST384p) sk_string = sk.to_string() #最短的调用,然后再重新创建私钥。to_string():将括号内的数字转化为字符串,实际返回的类型bytes sk2 = SigningKey.from_string(sk_string, curve = NIST384p) #重新创建私钥,第一个参数是我们要处理的字符,如果点编码无效或不在指定曲线上,from_string()将引发MalformedPointError print (sk_string. hex ()) print (sk2.to_string(). hex ()) |
1
2
3
4
5
|
from ecdsa import SigningKey, NIST384p sk = SigningKey.generate(curve = NIST384p) sk_pem = sk.to_pem() #sk.to_pem()和sk.to_der()将把签名密钥序列化为OpenSSL使用的相同格式 sk2 = SigningKey.from_pem(sk_pem) #SigningKey.from_pem()/.from_der()将撤销这种序列化。这些格式包括了曲线名称,所以你不需要向反序列化器传递曲线标识符。如果文件是畸形的,from_der()和from_pem()将引发UnexpectedDER或MalformedPointError。 # sk and sk2 are the same key |
1
2
3
4
5
6
|
from ecdsa import SigningKey, VerifyingKey, NIST384p sk = SigningKey.generate(curve = NIST384p) vk = sk.verifying_key vk_string = vk.to_string() #公钥可以用同样的方式进行序列化 vk2 = VerifyingKey.from_string(vk_string, curve = NIST384p) # vk and vk2 are the same key |
1
2
3
4
5
6
|
from ecdsa import SigningKey, VerifyingKey, NIST384p sk = SigningKey.generate(curve = NIST384p) vk = sk.verifying_key vk_pem = vk.to_pem() vk2 = VerifyingKey.from_pem(vk_pem) # vk and vk2 are the same key |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import os from ecdsa import NIST384p, SigningKey from ecdsa.util import randrange_from_seed__trytryagain #产生随机数 def make_key(seed): secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) return SigningKey.from_secret_exponent(secexp, curve = NIST384p) seed = os.urandom(NIST384p.baselen) # or other starting point,返回一个适合加密的比特串 sk1a = make_key(seed) sk1b = make_key(seed) # note: sk1a and sk1b are the same key assert sk1a.to_string() = = sk1b.to_string() sk2 = make_key(b "2-" + seed) # different key b为比特 assert sk1a.to_string() ! = sk2.to_string() from ecdsa import SigningKey, NIST384p sk = SigningKey.generate(curve = NIST384p) vk = sk.verifying_key vk.precompute() signature = sk.sign(b "message" ) assert vk.verify(signature, b "message" ) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# openssl ecparam -name prime256v1 -genkey -out sk.pem # openssl ec -in sk.pem -pubout -out vk.pem # echo "data for signing" > data # openssl dgst -sha256 -sign sk.pem -out data.sig data # openssl dgst -sha256 -verify vk.pem -signature data.sig data # openssl dgst -sha256 -prverify sk.pem -signature data.sig data #OpenSSL 使用 PEM 文件格式存储证书和密钥。PEM 实质上是 Base64 编码的二进制内容 import hashlib # from ecdsa import SigningKey, VerifyingKey from ecdsa.util import sigencode_der, sigdecode_der #从ecdsa.util写入和读取签名 with open ( "vk.pem" ) as f: #公钥文件 vk = VerifyingKey.from_pem(f.read()) with open ( "data" , "rb" ) as f: #open()为读取模式,with语句直接调用close方法,r为读模式,w/wb为写模式,rb模式打开二进制文件,消息data data = f.read() with open ( "data.sig" , "rb" ) as f: #消息签名可读模式 signature = f.read() assert vk.verify(signature, data, hashlib.sha256, sigdecode = sigdecode_der) #公钥验证签名, with open ( "sk.pem" ) as f: #私钥文件 sk = SigningKey.from_pem(f.read(), hashlib.sha256) new_signature = sk.sign_deterministic(data, sigencode = sigencode_der) #用私钥签名生成一个新的签名 with open ( "data.sig2" , "wb" ) as f: #写模式 f.write(new_signature) |
1
2
3
4
5
6
7
8
9
10
11
|
# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data #如果需要与OpenSSL 1.0.0或更早的版本兼容,可以使用ecdsa.util中的sigencode_string和sigdecode_string来分别写入和读取签名。 from ecdsa import SigningKey, VerifyingKey with open ( "sk.pem" ) as f: sk = SigningKey.from_pem(f.read()) with open ( "sk.pem" , "wb" ) as f: f.write(sk.to_pem()) with open ( "vk.pem" ) as f: vk = VerifyingKey.from_pem(f.read()) with open ( "vk.pem" , "wb" ) as f: f.write(vk.to_pem()) |
1
2
3
4
5
6
7
8
9
10
11
|
#ecdsa.util.PRNG 工具在这里很方便:它需要一个种子并从中产生一个强的伪随机流。 #os.urandom的函数作为entropy=参数来做不同的事情 #ECDSA的签名生成也需要一个随机数,而且每个签名都必须使用不同的随机数(两次使用相同的数字会立即暴露出私人签名密钥)。 # sk.sign()方法需要一个entropy=参数,其行为与SigningKey.generate(entropy=)相同。 from ecdsa.util import PRNG from ecdsa import SigningKey rng1 = PRNG(b "seed" ) sk1 = SigningKey.generate(entropy = rng1) rng2 = PRNG(b "seed" ) sk2 = SigningKey.generate(entropy = rng2) # sk1 and sk2 are the same key |
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
|
#如果你调用SigningKey.sign_deterministic(data)而不是.sign(data),代码将生成一个确定性的签名,而不是随机的。 # 这使用RFC6979中的算法来安全地生成一个唯一的K值,该值来自于私钥和被签名的信息。每次你用相同的密钥签署相同的信息时,你将得到相同的签名(使用相同的k)。 #创建一个NIST521p密钥对 from ecdsa import SigningKey, NIST521p sk = SigningKey.generate(curve = NIST521p) vk = sk.verifying_key #从一个主种子创建三个独立的签名密钥 from ecdsa import NIST192p, SigningKey from ecdsa.util import randrange_from_seed__trytryagain def make_key_from_seed(seed, curve = NIST192p): secexp = randrange_from_seed__trytryagain(seed, curve.order) return SigningKey.from_secret_exponent(secexp, curve) sk1 = make_key_from_seed( "1:%s" % seed) sk2 = make_key_from_seed( "2:%s" % seed) sk3 = make_key_from_seed( "3:%s" % seed) #从磁盘上加载一个验证密钥,并使用十六进制编码以未压缩和压缩的格式打印出来(在X9.62和SEC1标准中定义)。 from ecdsa import VerifyingKey with open ( "public.pem" ) as f: #加载验证密钥 vk = VerifyingKey.from_pem(f.read()) print ( "uncompressed: {0}" . format (vk.to_string( "uncompressed" ). hex ())) print ( "compressed: {0}" . format (vk.to_string( "compressed" ). hex ())) #从压缩格式的十六进制字符串中加载验证密钥,以未压缩的格式输出。 from ecdsa import VerifyingKey, NIST256p comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve = NIST256p) print (vk.to_string( "uncompressed" ). hex ()) #与远程方进行ECDH密钥交换。 from ecdsa import ECDH, NIST256p ecdh = ECDH(curve = NIST256p) ecdh.generate_private_key() local_public_key = ecdh.get_public_key() #send `local_public_key` to remote party and receive `remote_public_key` from remote party with open ( "remote_public_key.pem" ) as e: remote_public_key = e.read() ecdh.load_received_public_key_pem(remote_public_key) secret = ecdh.generate_sharedsecret_bytes() |
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/weixin_45748968/article/details/121464253