title: 32. 椭圆曲线加密 tags: zk abstract algebra elliptic curve group theory finite field discrete logarithm diffie-hellman elgamal ecdsa WTF zk 教程第 32 讲:椭圆曲线密码学 这一讲,我们将介绍椭圆曲线密码学中的两个经典算法:椭圆曲线 Diffie-Hellman 算法(ECDH),椭圆曲线 Elgamal 算法(EC Elgamal),和椭圆曲线数字签名算法(ECDSA)。它们都是椭圆曲线和传统公钥加密技术的结合,能够在更短的密钥长度下提供更高的安全性。
title: 32. 椭圆曲线加密 tags: - zk - abstract algebra - elliptic curve - group theory - finite field - discrete logarithm - diffie-hellman - elgamal - ecdsa
这一讲,我们将介绍椭圆曲线密码学中的两个经典算法:椭圆曲线 Diffie-Hellman 算法(ECDH),椭圆曲线 Elgamal 算法(EC Elgamal),和椭圆曲线数字签名算法(ECDSA)。它们都是椭圆曲线和传统公钥加密技术的结合,能够在更短的密钥长度下提供更高的安全性。
如果你不了解 Diffie-Hellman 算法或 Elgamal 算法,可以阅读 WTF zk 里程碑02和里程碑03。
椭圆曲线密码学(ECC)是一种基于椭圆曲线数学理论的密码学方法,以椭圆曲线上的点加法和标量乘法为基础。ECC 的优势在于它能在较短的密钥长度下提供与传统公钥加密体系相同或更高的安全性。
ECDH 算法是一种基于椭圆曲线的 Diffie-Hellman 密钥交换协议。它利用椭圆曲线离散对数问题(ECDLP)的难解性来实现安全的密钥交换,使得即使在公开信道上,攻击者也无法轻易推断出交换的密钥。
ECDH算法被广泛应用于许多安全通信标准和协议中,包括TLS/SSL安全传输协议、安全的即时通讯应用,和加密货币钱包。
ECDH 算法密钥交换过程和传统的 Diffie-Hellman 算法相似,只不过把整数乘法群换位了有限域上的椭圆曲线。
下面的Python代码中,我们使用secp256k1曲线进行 ECDH 密钥交换。
# 椭圆曲线Diffie-Hellman算法 ECDH from py_ecc.secp256k1 import secp256k1 import os def generate_keys(): private_key = int.from_bytes(os.urandom(32), 'big') % secp256k1.N public_key = secp256k1.multiply(secp256k1.G, private_key) return private_key, public_key # Alice和Bob生成各自的密钥对 alice_private, alice_public = generate_keys() bob_private, bob_public = generate_keys() # 计算共享密钥 shared_secret_alice = secp256k1.multiply(bob_public, alice_private) shared_secret_bob = secp256k1.multiply(alice_public, bob_private) if shared_secret_alice == shared_secret_bob: print("共享密钥匹配。") else: print("共享密钥不匹配!") print(f"Alice私钥: {alice_private}") print(f"Alice公钥: {alice_public}") print(f"Bob私钥: {bob_private}") print(f"Bob公钥: {bob_public}") print(f"共享密钥: {shared_secret_alice}") # 共享密钥匹配。 # Alice私钥: 44226773042722162955098193291492534006186517732096623157459837212766793078584 # Alice公钥: (113906392817926084413632896524344771269472367375880032535005632965062391078788, 49665636540644454541653315656482000530366349019751676160955522917215379042285) # Bob私钥: 51860882402071446551116109914681284224864199234652843480335793819475548437366 # Bob公钥: (52340819409831460217804635786419806447405367609650964443132838196582132856471, 56429557458241459690871510882159471830396052430769816127197158365607969924309) # 共享密钥: (39817116182924354378808003014233470575110979407770339130416639641795260327693, 42970388080766198583159133018251494914868250846130428856587988974064644921855)
椭圆曲线ElGamal加密算法(EC ElGamal)是融合椭圆曲线密码学(ECC)和ElGamal加密体系的一种公钥加密算法。它利用椭圆曲线离散对数问题(ECDLP)的难解性来实现安全的消息加密。
我们假设 Alice 要通过 EC ElGamal 算法跟 Bob 通信。
首先,Bob 需要生成密钥:
公钥为 (E, G, Y),是公开的;私钥为 x,不公开。
接下来,Alice 要用公钥加密消息明文 M,其中 M 是椭圆曲线上的一个点:
随机数 k 在每次加密都会变换,保证了 EC ElGamal 算法即使加密相同的明文也会输出不同的临时密文。在加密操作中,随机数 k 是隐私的,而密文 (C_1, C_2)是公开的。
Bob 收到密文 (C_1, C_2) 后,需要使用私钥 x 进行解密:
计算得到的点 M' 即为原始消息明文 M。
下面的Python代码中,我们使用secp256k1曲线进行 EC Elgamal 加密。
from py_ecc.secp256k1 import secp256k1 from random import randint def elgamal_encrypt(G, Y, M): k = randint(1, secp256k1.N - 1) C1 = secp256k1.multiply(G, k) C2 = secp256k1.add(M, secp256k1.multiply(Y, k)) return (C1, C2) def elgamal_decrypt(C1, C2, x): # 使用私钥x计算xC1 xC1 = secp256k1.multiply(C1, x) # 计算M = C2 - xC1 M = secp256k1.add(C2, (xC1[0], -xC1[1])) return M # 示例参数 p = secp256k1.N G = secp256k1.G # 生成密钥对 x, Y = generate_keys() # 假设消息M是曲线上的一个点,这里简单选择G作为示例 M = G print("原始消息明文:", M) # 加密 C1, C2 = elgamal_encrypt(G, Y, M) print("加密后的消息:", (C1, C2)) # 解密 M_decrypted = elgamal_decrypt(C1, C2, x) print("解密后的消息:", M_decrypted) # 验证 assert M == M_decrypted, "解密失败!" print("消息成功解密!") # 示例输出 # 原始消息明文: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424) # 加密后的消息: ((87298472810248234319752437423707505477343664832890363292431829216099637291919, 39528614830056678009484946030376271359657183017625571564228160252781333158439), (67113196324182438503834247973075313606138491143388276462715763950508942145812, 59499979624168470896804403233074133393632477568643779021536973756576878140912)) # 解密后的消息: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424) # 消息成功解密!
ECDSA(椭圆曲线数字签名算法)是椭圆曲线密码学中的一种常用数字签名算法,它利用 ECDLP 的难解性来确保签名的安全性,常被用于以太坊的智能合约中,见 WTF Solidity 第37讲。
我们假设 Bob 使用 ECDSA 进行签名,然后 Alic 验证。主要三个步骤:密钥生成、签名、验证签名。
首先,Bob 需要生成密钥:
公钥为 (E, G, Y),是公开的;私钥为 x,不公开。
接下来,Bob 要使用刚刚生成的私钥 x 进行签名:
消息哈希:计算消息 M 的哈希值 H(M)
选择随机数:生成一个随机数 k,确保 1 < k < p-1。
计算 r:通过随机数 k 计算点 P = kG,将点 P 的横坐标记为 r。
生成签名:计算 s=k^{-1}(H(M)+xr) \mod n,其中 n 是基点 G 的阶。生成的签名为 (r, s)。
生成签名要确保生成随机生成 k,不然私钥可能会泄露。另外,如果 r 或 s 为零,则需要重新生成随机数 k。
这一步,Alice 利用消息原文 M 和公钥 Y 来验证签名的有效性。
计算消息哈希:计算 H(M)。
计算 u_1 和 u_2:计算 u_1=H(m)s^{-1} \mod n 和 u_2=rs^{-1} \mod n。
验证签名:计算点 P'=u_1G+u_2Y,如果 P' 的横坐标等于 r,则签名有效。
咱们来看下为什么正确签名得到的 P' 横坐标会和 r 相等:
使用标量乘法的分配律,有:
展开 u_1 和 u_2,有:
展开 s,有
因此,正确签名得到的 P' 的横坐标与 r 相等。
下面的Python代码中,我们使用secp256k1曲线来实现ECDSA的签名和验证过程:
# ECDSA from py_ecc.secp256k1 import secp256k1 import os import hashlib def generate_keys(): # 生成私钥 private_key = os.urandom(32) private_key_int = int.from_bytes(private_key, 'big') % secp256k1.N # 生成公钥 public_key = secp256k1.multiply(secp256k1.G, private_key_int) return private_key_int, public_key def ecdsa_sign(message, private_key): # 对消息进行哈希处理 message_hash = hashlib.sha256(message).digest() message_hash_int = int.from_bytes(message_hash, 'big') k = int.from_bytes(os.urandom(32), 'big') % secp256k1.N R = secp256k1.multiply(secp256k1.G, k) r = R[0] % secp256k1.N s = ((message_hash_int + r * private_key) * secp256k1.inv(k, secp256k1.N)) % secp256k1.N return (r, s) def ecdsa_verify(message, signature, public_key): r, s = signature message_hash = hashlib.sha256(message).digest() message_hash_int = int.from_bytes(message_hash, 'big') w = secp256k1.inv(s, secp256k1.N) u1 = (message_hash_int * w) % secp256k1.N u2 = (r * w) % secp256k1.N P = secp256k1.add(secp256k1.multiply(secp256k1.G, u1), secp256k1.multiply(public_key, u2)) return r == P[0] % secp256k1.N # 示例 x, Y = generate_keys() M = b"Hello, ECDSA with secp256k1!" print("原始消息明文:", M) signature = ecdsa_sign(M, x) print("签名:", signature) valid = ecdsa_verify(M, signature, Y) print("签名验证结果:", valid)
这一讲,我们介绍了椭圆曲线密码学中的两个经典算法:ECDH 和 EC Elgamal。它们都是椭圆曲线和传统公钥密码学算法的结合,可以在较短的密钥长度下提供更高的安全性,非常适合区块链和零知识证明的使用场景。