如何在PyCryptodome中用ECC替代RSA实现混合加密
如何用PyCryptodome实现ECC+AES混合加密
你遇到的问题很典型——PKCS1_OAEP是专门为RSA设计的加密模式,完全不能和ECC公钥配合使用!在ECC体系下,要安全地加密AES会话密钥,我们通常采用两种方案:ECIES(椭圆曲线集成加密方案)(贴近你原有的“用公钥加密密钥”思路),或者通过ECDH密钥交换生成共享会话密钥。下面给你详细的修正实现:
方案一:手动实现ECDH+AES混合加密
这种方式不需要依赖第三方库,完全基于PyCryptodome原生功能实现,最贴合你原有的代码逻辑:
from Crypto.PublicKey import ECC from Crypto.Random import get_random_bytes from Crypto.Cipher import AES from Crypto.Hash import SHA256 from Crypto.Protocol.KDF import HKDF import base64 def generate_keys(): # 生成P-256曲线的ECC密钥(安全强度等效于3072位RSA) key = ECC.generate(curve='P-256') private_key = key.export_key(format='PEM') with open('private_key.pem', 'wt') as f: f.write(private_key) public_key = key.public_key().export_key(format='PEM') with open('public_key.pem', 'wt') as f: f.write(public_key) def encrypt(username, msg): # 读取接收方的ECC公钥 with open(f"{username}.pem", 'rt') as f: recipient_pub_key = ECC.import_key(f.read()) # 步骤1:生成临时ECC密钥对,用于ECDH密钥交换 ephemeral_key = ECC.generate(curve='P-256') # 计算双方共享的秘密值 shared_secret = ephemeral_key.d * recipient_pub_key.pointQ # 用HKDF派生安全的AES会话密钥(避免直接使用原始共享值) derived_key = HKDF(shared_secret.x.to_bytes(32, byteorder='big'), 16, salt=None, hashmod=SHA256) # 步骤2:用派生密钥加密明文(和原AES逻辑一致) cipher_aes = AES.new(derived_key, AES.MODE_EAX) ciphertext, tag = cipher_aes.encrypt_and_digest(msg.encode('utf-8')) # 步骤3:打包加密数据(临时公钥 + 分隔符 + nonce + tag + 密文) ephemeral_pub = ephemeral_key.public_key().export_key(format='PEM').encode('utf-8') encrypted_data = ephemeral_pub + b'||' + cipher_aes.nonce + tag + ciphertext encrypted_data = base64.b64encode(encrypted_data) return encrypted_data.decode() def decrypt(encrypted_data): # 读取自己的ECC私钥 with open('private_key.pem', 'rt') as f: private_key = ECC.import_key(f.read()) # 解码并拆分加密数据 encrypted_data = base64.b64decode(encrypted_data) ephemeral_pub_bytes, rest = encrypted_data.split(b'||', 1) nonce = rest[:16] tag = rest[16:32] ciphertext = rest[32:] # 导入临时公钥,计算共享密钥 ephemeral_pub = ECC.import_key(ephemeral_pub_bytes) shared_secret = private_key.d * ephemeral_pub.pointQ derived_key = HKDF(shared_secret.x.to_bytes(32, byteorder='big'), 16, salt=None, hashmod=SHA256) # AES解密并验证完整性 cipher_aes = AES.new(derived_key, AES.MODE_EAX, nonce=nonce) plaintext = cipher_aes.decrypt_and_verify(ciphertext, tag) return plaintext.decode('utf-8')
关键修改说明:
- 移除了错误的
PKCS1_OAEP调用,改用ECDH密钥交换生成共享密钥,再通过HKDF派生安全的AES会话密钥 - 生成临时ECC密钥对并将其公钥随加密数据一起发送,确保接收方能用自己的私钥计算出相同的共享密钥
- 用
||作为简单分隔符区分临时公钥和加密数据段(生产环境建议用更严谨的二进制格式,比如ASN.1)
方案二:用ECIES简化实现(第三方库辅助)
如果不想手动实现密钥交换逻辑,可以用eciespy库(需提前安装),它封装了成熟的ECIES实现:
pip install eciespy
简化后的代码如下:
from ecies import encrypt, decrypt from ecies.utils import generate_eth_key import base64 def generate_keys(): # 生成secp256k1曲线的密钥(也支持P-256等标准曲线) key = generate_eth_key() private_key = key.to_hex() public_key = key.public_key.to_hex() with open('private_key.txt', 'wt') as f: f.write(private_key) with open('public_key.txt', 'wt') as f: f.write(public_key) def encrypt(username, msg): # 读取接收方公钥 with open(f"{username}.txt", 'rt') as f: recipient_pub_key = f.read().strip() # ECIES自动完成会话密钥加密+明文加密的全流程 encrypted_bytes = encrypt(recipient_pub_key, msg.encode('utf-8')) return base64.b64encode(encrypted_bytes).decode() def decrypt(encrypted_data): # 读取自己的私钥 with open('private_key.txt', 'rt') as f: private_key = f.read().strip() encrypted_bytes = base64.b64decode(encrypted_data) plaintext = decrypt(private_key, encrypted_bytes) return plaintext.decode('utf-8')
核心原理说明
RSA基于大整数分解难题,而ECC基于椭圆曲线离散对数难题,两者数学基础完全不同,因此RSA的加密模式(如PKCS1_OAEP)无法直接应用在ECC上。ECC的核心优势就是用更短的密钥长度达到和RSA相同的安全强度,所以必须使用ECC专属的密钥交换或加密方案。
内容的提问来源于stack exchange,提问作者Ahmed Salama
相关产品推荐
相关产品推荐

