安全加解密引擎基础(PKE ECC/ECDH/ECDSA)
关键词: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的使用。