如何在python中實現(xiàn)ECDSA你知道嗎
import six import timeit#查找任何特定代碼執(zhí)行的確切時間 from ecdsa.curves import curves
#定義do函數(shù),計算時間 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為數(shù)字測試套件關于NIST詳解
GF§ (素數(shù)域)曲線,密鑰長度為192、224、256、384和521bit
OpenSSL工具(openssl ecparam -list_curves
)所知道的這些曲線的 "簡稱 "是:prime192v1
、secp224r1
、prime256v1
、secp384r1
和secp521r1
。它包括比特幣使用的256位曲線secp256k1。它還支持160到512位的Brainpool曲線的常規(guī)(非扭曲)變體。這些曲線的 "簡稱 "是:BrainpoolP160r1, brainpoolP192r1, brainpoolP224r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1, brainpoolP512r1。少數(shù)來自SEC標準的小曲線也包括在內(主要是為了加快庫的測試),它們是:secp112r1, secp112r2, secp128r1, 和secp160r1。沒有包括其他的曲線,但要增加對更多素數(shù)域的曲線的支持并不難。
#不是很懂 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()函數(shù) S6 = "vk.precompute()"#不懂 S7 = "vk.verify(sig, msg)"#用公鑰驗證簽名 # 我們碰巧知道.generate()也在計算驗證密鑰,這是最耗時的部分。如果將代碼改為懶惰地計算vk,我們就需要將這個基準改為在S5上循環(huán),而不是在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)、簽署數(shù)據(jù)(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",#小數(shù)點后面為5位 form_inv=".2f",#小數(shù)點后面為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": 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, ) )
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為一斷言函數(shù):不滿足條件直接觸發(fā)異常忙不執(zhí)行接下來的代碼,括號中為condition
from ecdsa import SigningKey, NIST384p#384位NIST素域橢圓曲線,其中私鑰/公鑰都與特定的曲線相關聯(lián),更長的曲線更安全,但時間長,密鑰和簽名也長 sk = SigningKey.generate(curve=NIST384p) vk = sk.verifying_key signature = sk.sign(b"message") assert vk.verify(signature, b"message")
#將簽名密鑰(私鑰)序列化成不同的格式。 from ecdsa import SigningKey, NIST384p sk = SigningKey.generate(curve=NIST384p) sk_string = sk.to_string()#最短的調用,然后再重新創(chuàng)建私鑰。to_string():將括號內的數(shù)字轉化為字符串,實際返回的類型bytes sk2 = SigningKey.from_string(sk_string, curve=NIST384p)#重新創(chuàng)建私鑰,第一個參數(shù)是我們要處理的字符,如果點編碼無效或不在指定曲線上,from_string()將引發(fā)MalformedPointError print(sk_string.hex()) print(sk2.to_string().hex())
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()將引發(fā)UnexpectedDER或MalformedPointError。 # sk and sk2 are the same key
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
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
import os from ecdsa import NIST384p, SigningKey from ecdsa.util import randrange_from_seed__trytryagain#產生隨機數(shù) 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")
# 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)
# 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())
#ecdsa.util.PRNG 工具在這里很方便:它需要一個種子并從中產生一個強的偽隨機流。 #os.urandom的函數(shù)作為entropy=參數(shù)來做不同的事情 #ECDSA的簽名生成也需要一個隨機數(shù),而且每個簽名都必須使用不同的隨機數(shù)(兩次使用相同的數(shù)字會立即暴露出私人簽名密鑰)。 # sk.sign()方法需要一個entropy=參數(shù),其行為與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
#如果你調用SigningKey.sign_deterministic(data)而不是.sign(data),代碼將生成一個確定性的簽名,而不是隨機的。 # 這使用RFC6979中的算法來安全地生成一個唯一的K值,該值來自于私鑰和被簽名的信息。每次你用相同的密鑰簽署相同的信息時,你將得到相同的簽名(使用相同的k)。 #創(chuàng)建一個NIST521p密鑰對 from ecdsa import SigningKey, NIST521p sk = SigningKey.generate(curve=NIST521p) vk = sk.verifying_key #從一個主種子創(chuàng)建三個獨立的簽名密鑰 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()
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注本站的更多內容!
版權聲明:本站文章來源標注為YINGSOO的內容版權均為本站所有,歡迎引用、轉載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內容涉嫌侵權,請聯(lián)系alex-e#qq.com處理。