import siximport timeit#查找任何特定代码执行的确切时间from ecdsa.curves import curves
#定义do函数,计算时间def do(setup_statements, statement):# extracted from timeit.pyt = timeit.Timer(stmt=statement, setup=\"\\n\".join(setup_statements))# determine number so that 0.2 <= total time < 2.0for i in range(1, 10):number = 10 ** i #**为次方x = t.timeit(number)if x >= 0.2:breakreturn 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。没有包括其他的曲线,但要增加对更多素数域的曲线的支持并不难。
#不是很懂 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\" % curveS2 = \"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 ecdsac = 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位))
print(\"\")
ED25519和Cureve5519
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\":continueS1 = \"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,))
from ecdsa import SigningKeysk = SigningKey.generate() # uses NIST192p生成私钥vk = sk.verifying_key#在私钥的基础上生成公钥signature = sk.sign(b\"message\")#用私钥对消息进行签名assert vk.verify(signature, b\"message\")#用公钥去验证。assert为一断言函数:不满足条件直接触发异常忙不执行接下来的代码,括号中为condition
from ecdsa import SigningKey, NIST384p#384位NIST素域椭圆曲线,其中私钥/公钥都与特定的曲线相关联,更长的曲线更安全,但时间长,密钥和签名也长sk = SigningKey.generate(curve=NIST384p)vk = sk.verifying_keysignature = sk.sign(b\"message\")assert vk.verify(signature, b\"message\")
#将签名密钥(私钥)序列化成不同的格式。from ecdsa import SigningKey, NIST384psk = SigningKey.generate(curve=NIST384p)sk_string = sk.to_string()#最短的调用,然后再重新创建私钥。to_string():将括号内的数字转化为字符串,实际返回的类型bytessk2 = SigningKey.from_string(sk_string, curve=NIST384p)#重新创建私钥,第一个参数是我们要处理的字符,如果点编码无效或不在指定曲线上,from_string()将引发MalformedPointErrorprint(sk_string.hex())print(sk2.to_string().hex())
from ecdsa import SigningKey, NIST384psk = 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
from ecdsa import SigningKey, VerifyingKey, NIST384psk = SigningKey.generate(curve=NIST384p)vk = sk.verifying_keyvk_string = vk.to_string()#公钥可以用同样的方式进行序列化vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p)# vk and vk2 are the same key
from ecdsa import SigningKey, VerifyingKey, NIST384psk = SigningKey.generate(curve=NIST384p)vk = sk.verifying_keyvk_pem = vk.to_pem()vk2 = VerifyingKey.from_pem(vk_pem)# vk and vk2 are the same key
import osfrom ecdsa import NIST384p, SigningKeyfrom 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 keyassert 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, NIST384psk = SigningKey.generate(curve=NIST384p)vk = sk.verifying_keyvk.precompute()signature = sk.sign(b\"message\")assert vk.verify(signature, b\"message\")
# 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, VerifyingKeyfrom 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模式打开二进制文件,消息datadata = 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)
# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data#如果需要与OpenSSL 1.0.0或更早的版本兼容,可以使用ecdsa.util中的sigencode_string和sigdecode_string来分别写入和读取签名。from ecdsa import SigningKey, VerifyingKeywith 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())
#ecdsa.util.PRNG 工具在这里很方便:它需要一个种子并从中产生一个强的伪随机流。#os.urandom的函数作为entropy=参数来做不同的事情#ECDSA的签名生成也需要一个随机数,而且每个签名都必须使用不同的随机数(两次使用相同的数字会立即暴露出私人签名密钥)。# sk.sign()方法需要一个entropy=参数,其行为与SigningKey.generate(entropy=)相同。from ecdsa.util import PRNGfrom ecdsa import SigningKeyrng1 = PRNG(b\"seed\")sk1 = SigningKey.generate(entropy=rng1)rng2 = PRNG(b\"seed\")sk2 = SigningKey.generate(entropy=rng2)# sk1 and sk2 are the same key
#如果你调用SigningKey.sign_deterministic(data)而不是.sign(data),代码将生成一个确定性的签名,而不是随机的。# 这使用RFC6979中的算法来安全地生成一个唯一的K值,该值来自于私钥和被签名的信息。每次你用相同的密钥签署相同的信息时,你将得到相同的签名(使用相同的k)。#创建一个NIST521p密钥对from ecdsa import SigningKey, NIST521psk = SigningKey.generate(curve=NIST521p)vk = sk.verifying_key#从一个主种子创建三个独立的签名密钥from ecdsa import NIST192p, SigningKeyfrom ecdsa.util import randrange_from_seed__trytryagaindef 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 VerifyingKeywith 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, NIST256pcomp_str = \'022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759\'vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p)print(vk.to_string(\"uncompressed\").hex())#与远程方进行ECDH密钥交换。from ecdsa import ECDH, NIST256pecdh = 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 partywith 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()
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
您可能感兴趣的文章:
- Java实现ECDSA签名算法
- eclipse实现ECDSA数字签名
- 详解Node.js 中使用 ECDSA 签名遇到的坑