關鍵詞:ECC、ECDH、ECDSA、PyCryptodome、ecdsa、OpenSSL等。
1 基本概念
1.1 ECC
Elliptic Curves Cryptography,橢圓曲線密碼學(英語:Elliptic curve cryptography,縮寫為ECC),一種建立公開密鑰加密的算法,基於橢圓曲線數學。橢圓曲線在密碼學中的使用是在1985年由Neal Koblitz和Victor Miller分別獨立提出的。
1.1.1 ECC參數
ECC的參數可以有很多,通過openssl ecparam -list_curves查看:
... secp160r1 : SECG curve over a 160 bit prime field secp224r1 : NIST/SECG curve over a 224 bit prime field secp384r1 : NIST/SECG curve over a 384 bit prime field secp521r1 : NIST/SECG curve over a 521 bit prime field brainpoolP160r1: RFC 5639 curve over a 160 bit prime field brainpoolP192r1: RFC 5639 curve over a 192 bit prime field brainpoolP224r1: RFC 5639 curve over a 224 bit prime field brainpoolP256r1: RFC 5639 curve over a 256 bit prime field brainpoolP384r1: RFC 5639 curve over a 384 bit prime field brainpoolP512r1: RFC 5639 curve over a 512 bit prime field SM2 : SM2 curve over a 256 bit prime field
更多參考:
《SECG | Standard curve database (neuromancer.sk)》-SEC 2: Recommended Elliptic Curve Domain Parameters version 2.0 January 27, 2010
《SEC 2, ver. 2.0 (secg.org)》-SEC 2: Recommended Elliptic Curve Domain Parameters
1.2 ECDH
橢圓曲線迪菲-赫爾曼金鑰交換(英語:Elliptic Curve Diffie–Hellman key Exchange,縮寫為ECDH),一種匿名的密鑰合意協議(Key-agreement protocol)。
在這個協定下,雙方通過迪菲-赫爾曼密鑰交換算法,利用由橢圓曲線加密建立的公鑰與私鑰對,在一個不安全的通道中,建立起安全的共有加密資料。這是迪菲-赫爾曼密鑰交換的變種,采用橢圓曲線加密來加強安全性。
ECDH全稱是橢圓曲線迪菲-赫爾曼秘鑰交換(Elliptic Curve Diffie–Hellman key Exchange),主要是用來在一個不安全的通道中建立起安全的共有加密資料,一般來說交換的都是私鑰,這個密鑰一般作為“對稱加密”的密鑰而被雙方在后續數據傳輸中使用。ECDH是ECC算法和DH結合使用,用於密鑰磋商,這個密鑰交換算法稱為ECDH。交換雙方可以在不共享任何秘密的情況下協商出一個密鑰。ECC是建立在基於橢圓曲線的離散對數問題上的密碼體制。
1.2.1 ECDH流程

1.3 ECDSA
ECDSA(橢圓曲線數字簽名算法)是DSA(數字簽名算法)的橢圓曲線實現。橢圓曲線密碼術能夠以較小的密鑰提供與RSA相對相同的安全級別。它還具有DSA對不良RNG敏感的缺點。
ECDSA是數字簽名算法,是DSA的變體。在數字簽名算法中,消息發送方對消息進行簽名,消息接收方對消息驗簽,這樣能夠保證數據的完整性(保證消息內容未被第三方篡改)、消息源鑒別(確定消息是由本人發出,而不是他人偽造)和不可否認性(消息發送方無法否認自己發出過這則消息)。
1.3.1 ECDSA流程

更多參考:
《淺談ECC&ECDH&ECDSA》-介紹了ECC、ECDH、ECDSA基本概念和基本原理。
2 openssl關於ECC/DECDH/ECDSA的使用
生成ECC私鑰和公鑰:
openssl ecparam -name prime256v1 -genkey -out sk.pem openssl ec -in sk.pem -pubout -out vk.pem
生成ECC簽名:
openssl dgst -sha256 -sign sk.pem -out data.sig data
ECC驗簽:
openssl dgst -sha256 -verify vk.pem -signature data.sig data
將Python ecdsa和OpenSSL對比測試如下:
import hashlib from ecdsa import SigningKey, VerifyingKey from ecdsa.util import sigencode_der, sigdecode_der import os if __name__ == '__main__': os.system("openssl ecparam -name prime256v1 -genkey -out sk.pem") os.system("openssl ec -in sk.pem -pubout -out vk.pem") os.system("echo \"data for signing\" > data") os.system("openssl dgst -sha256 -sign sk.pem -out data.sig data") os.system("openssl dgst -sha256 -verify vk.pem -signature data.sig data") os.system("openssl dgst -sha256 -prverify sk.pem -signature data.sig data") with open("vk.pem") as f: vk = VerifyingKey.from_pem(f.read()) with open("data", "rb") as f: 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) os.system("openssl dgst -sha256 -verify vk.pem -signature data.sig2 data")
3 Python中PyCryptodome關於ECC、ECDSA和ecdsa關於ECDH的使用
3.1 PyCryptodome關於ECC和ECDSA使用
首先使用PyCryptodome生成ECC私鑰和公鑰,然后使用DSA進行簽名和驗簽:
from Crypto.Hash import SHA256 from Crypto.PublicKey import ECC from Crypto.Signature import DSS if __name__ == '__main__': key = ECC.generate(curve='secp256r1') with open('myprikey.pem', 'wt') as f: f.write(key.export_key(format='PEM')) f.close() with open('mypubkey.pem', 'wt') as f: f.write(key.public_key().export_key(format='PEM')) f.close() message = b'This is a sample.' key = ECC.import_key(open('myprikey.pem').read()) h = SHA256.new(message) signer = DSS.new(key, 'fips-186-3') signature = signer.sign(h) key = ECC.import_key(open('mypubkey.pem').read()) verifier = DSS.new(key, 'fips-186-3') try: verifier.verify(h, signature) print("The message is authentic.") except ValueError: print("The message is not authentic.")
關於ECC秘鑰參數有如下:
| Curve | Possible identifiers |
|---|---|
| NIST P-192 | 'NIST P-192', 'p192', 'P-192', 'prime192v1', 'secp192r1' |
| NIST P-224 | 'NIST P-224', 'p224', 'P-224', 'prime224v1', 'secp224r1' |
| NIST P-256 | 'NIST P-256', 'p256', 'P-256', 'prime256v1', 'secp256r1' |
| NIST P-384 | 'NIST P-384', 'p384', 'P-384', 'prime384v1', 'secp384r1' |
| NIST P-521 | 'NIST P-521', 'p521', 'P-521', 'prime521v1', 'secp521r1' |
更多參考:
《ECC — PyCryptodome 3.14.1 documentation》-關於ECC秘鑰生成。
《Digital Signature Algorithm (DSA and ECDSA) — PyCryptodome 3.14.1 documentation》-關於ECDSA的使用方法。
3.2 ecdsa關於ECDSA和ECDH的使用
使用ecdsa進行簽名驗簽:
from ecdsa import SigningKey, NIST384p if __name__ == '__main__': sk = SigningKey.generate(curve=NIST384p) vk = sk.verifying_key signature = sk.sign(b"This is a sample") assert vk.verify(signature, b"This is a sample")
使用ecdsa進行ECDH密鑰磋商:
from ecdsa import ECDH, NIST256p # Press the green button in the gutter to run the script. if __name__ == '__main__': ecdh = ECDH(curve=NIST256p) ecdh.generate_private_key() local_public_key = ecdh.get_public_key() with open('remote_public_key.pem', 'wb') as f: f.write(local_public_key.to_pem()) f.close() # 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) try: secret = ecdh.generate_sharedsecret_bytes() print("Generate shared secret from local private key and remote public key.") except: print("public_key curve not the same as self.curve or public_key or private_key is not set")
更多參考:
《ecdsa · PyPI》-介紹了ECDH和ECDSA的使用。
