title: Milestone 03. ElGamal算法 tags: zk basic cryptography elgamal WTF zk 教程 里程碑 03:ElGamal 算法 在这一讲中,我们将介绍 ElGamal 加密和签名算法。ElGamal 是一种基于离散对数问题的公钥密码学算法,由 ElGamal 在 1985 年提出,将 Diffie-Hellman 密钥交换算法推广到了加密和数字签名领域。 ElGamal 加密算法 ElGamal 算法是一种公钥密码学的算法,其安全性基于计算离散对数的困难性。ElGamal 算法包括加密和数字签名两个部分,我们来看加密算法的流程。 我们假设 Alice 要通过 ElGamal 算法跟 Bob 通信。 1.
title: Milestone 03. ElGamal算法 tags: - zk - basic - cryptography - elgamal
在这一讲中,我们将介绍 ElGamal 加密和签名算法。ElGamal 是一种基于离散对数问题的公钥密码学算法,由 ElGamal 在 1985 年提出,将 Diffie-Hellman 密钥交换算法推广到了加密和数字签名领域。

ElGamal 算法是一种公钥密码学的算法,其安全性基于计算离散对数的困难性。ElGamal 算法包括加密和数字签名两个部分,我们来看加密算法的流程。
我们假设 Alice 要通过 ElGamal 算法跟 Bob 通信。
Bob 使用 ElGamal 算法的密钥生成包括以下步骤:
最终,公钥为 (p, g, y),是公开的;私钥为 x,不公开。
Alice 在获取公钥 (p, g, y) 后使用 ElGamal 加密,过程如下:
随机数 k 在每次加密都会变换,保证了 ElGamal 算法即使加密相同的明文也会输出不同的临时密文。在加密操作中,私钥 x 和随机数 k 是隐私的,而公钥 (p, g, y) 和密文 (a, b),是公开的。
Bob 在收到密文 (a, b) 后,使用 ElGamal 解密过程如下:
ElGamal 算法就巧妙在 b \cdot s^{-1} 能够还原出原文,而计算 s 需要私钥 x 的信息。没有私钥,窃听者想得到明文,就要解离散对数问题(解不出来)。
让我们通过一个简单的例子来说明 ElGamal 加密算法。
假设 p = 13,g = 6 是 p 的原根,私钥 x = 4,公钥 y = g^x = 9 。密钥生成后,Bob 将 (p, g, y) 公开。
现在,Alice 希望加密消息 M = 5。Alice 随机选择 k = 7(注意要跟 p 互质),并计算:
因此,密文为 (7, 6)。
接下来,Bob 收到密文,并解密:
最终,Bob 成功解密,还原出原始消息 M = 5。
在介绍算法之前,我们先介绍什么是数字签名。传统行业中,人们会在合同签上纸质签名,具有法律效应。数字签名是一种用于确保数字信息完整性、认证发送方身份以及防止抵赖的技术。数字签名通过使用加密算法生成一个独特的标识符(签名),该标识符附加到消息或文档上。这个数字签名可以被验证,以确认消息是由特定的发送方生成,并且在传输过程中没有被篡改。
数字签名通常涉及两个关键的密钥:私钥和公钥。发送方使用私钥对消息进行签名,而接收方使用公钥验证签名的有效性。这种方法建立在非对称加密的基础上,其中私钥用于签名生成,而公钥用于验证签名。
数字签名具有以下关键属性:
与 ElGamal 加密算法相似,ElGamal 签名算法也使用了离散对数问题的困难性来保证签名的安全性。算法主要分为密钥生成、签名生成和签名验证三个步骤,我们用 Alice(签名方)和 Bob(验证方)做演示。
Alice 生成密钥,用于签名:
密钥生成步骤和 ElGamal 加密算法相同,最终的公钥为(p, g, y),公开的;私钥为x,隐私的。
Alice 利用密钥和消息哈希生成签名:
如果 s=0,那么需要重新生成一个随机数 k 再次计算。最初论文里用的不是哈希 H(M) 而是消息 M 本身,但这会带来安全问题。最终的签名为 (r, s),公开的。
Bob 可以利用公开的信息 (g, p, r, s, M) 来验证签名是否真实。
因为 y^rr^s = g^{xr}r^{s}=g^{xr}g^{ks} = g^{xr+ks},而 xr+ks = xr +k(k^{-1}(H(M) - xr)) = H(M) \pmod{p-1}。所以,如果 g^{H(M)} \equiv y^rr^s \pmod{p},也就是 xr+ks = H(M) \pmod{p-1} 成立,那么签名就是有效的。
让我们通过一个简单的例子来说明 ElGamal 签名算法。假设我们已经有一个密钥对 (p, g, x, y),其中 p = 13,g = 6,x = 4,y = 9 与签名消息哈希 H(M) = 5。
签名时,我们随机选择 k = 7,它与 p-1 = 12 互质,计算:
因此,签名为 (7, 7)。
接下来,我们验证签名是否有效:
g^{H(M)} \equiv y^rr^s \pmod{p} 计算比较复杂,我们可以直接验证 xr+ks = H(M) \mod 12 是否成立。 xr+ks = 4 \times 7 + 7 \times 7 = 77 = 5 = H(M) \mod 12,g^{5} \equiv g^{77} \equiv 2 \pmod{13}因此签名有效。
## ElGamal 加密算法 from random import randint from sympy import isprime, mod_inverse def generate_keys(): # 生成大素数 p 和原根 g while True: p = randint(1000, 2000) if isprime(p): break g = randint(2, p-1) # 私钥 x x = randint(1, p-2) # 公钥 y y = pow(g, x, p) return (p, g, y), x def encrypt(public_key, message): p, g, y = public_key k = randint(1, p-2) # 加密 C1 = pow(g, k, p) C2 = (message * pow(y, k, p)) % p return (C1, C2) def decrypt(private_key, p, ciphertext): C1, C2 = ciphertext a = private_key # 解密 s = pow(C1, a, p) m = (C2 * mod_inverse(s, p)) % p return m # 示例 public_key, private_key = generate_keys() # 生成密钥 message = 123 # 消息明文 ciphertext = encrypt(public_key, message) #密文 decrypted_message = decrypt(private_key, public_key[0], ciphertext) # 解密 print("公钥 (p, g, y):", public_key) print("私钥 x:", private_key) print("消息明文:", message) print("密文:", ciphertext) print("解密还原明文:", decrypted_message) ## 输出样例 # 公钥 (p, g, y): (1307, 643, 698) # 私钥 x: 11 # 消息明文: 123 # 密文: (690, 1225) # 解密还原明文: 123
## ElGamal 签名算法 from sympy import gcd def generate_keys_signature(): # 和 ElGamal 加密算法一样 return generate_keys() def sign(private_key, p, g, message): while True: k = randint(1, p-1) if gcd(k, p-1) == 1: # k 与 p-1 互质 break r = pow(g, k, p) x = private_key s = ((message - x * r) * mod_inverse(k, p-1)) % (p-1) return (r, s) def verify(public_key, p, g, message, signature): y = public_key[2] r, s = signature if not (0 < r < p) or not (0 < s < p-1): return False return pow(g, message, p) == (pow(y, r, p) * pow(r, s, p)) % p # 示例 public_key, private_key = generate_keys_signature() # 生成密钥 message = 123 # 消息明文 signature = sign(private_key, public_key[0], public_key[1], message) # 生成签名 verification_result = verify(public_key, public_key[0], public_key[1], message, signature) # 验证签名 public_key, private_key, signature, verification_result print("公钥 (p, g, y):", public_key) print("私钥 x:", private_key) print("消息明文:", message) print("签名:", signature) print("验证结果:", verification_result) ## 输出样例 # 公钥 (p, g, y): (1409, 853, 1193) # 私钥 x: 1035 # 消息明文: 123 # 签名: (1126, 1403) # 验证结果: True
这一讲,我们介绍了 ElGamal 算法,它将 Diffie-Hellman 算法的思想推广到了加密和数字签名领域。和 Diffie-Hellman 算法一样,ElGamal 算法的安全性也基于离散对数问题的困难性。
思考题:为什么在 ElGamal 签名算法中,签名 s 的计算是在模 p-1 下的?