基于ElGamal算法的加密与签名机制
1. 背景知识
ElGamal算法是基于DH密钥交换的非对称加密算法1,是基于离散对数困难问题。
离散对数困难表述为,已知群
G
G
G的阶为
p
p
p(
p
p
p为素数),及其生成元
g
g
g,当给出
y
=
g
x
m
o
d
p
y=g^x\; mod\; p
y=gxmodp和
g
g
g要求出
x
x
x是困难的,当
p
p
p很大时在计算上是不可能的。ElGamal作为非对称密码机制的一种,可以实现加密与签名。
2. ElGamal加解密
ElGamal作为加密算法时主要有3个功能,分别为KeyGen,Enc,Dec,即密钥生成,加密,解密。
KeyGen:
- 选定应该大素数 p p p,和 Z p ∗ Z_p^* Zp∗的生成元 g g g。
- 随机选择一个整数 x ∈ Z p ∗ x\in Z_p^* x∈Zp∗,计算 y = g x m o d p y=g^x\ mod\ p y=gx mod p。
- 将 ( y , g , p ) (y,g,p) (y,g,p)作为公钥, x x x作为私钥。
Enc:
- 设明文消息为m,随机选择 r ∈ Z p ∗ r\in Z_p^* r∈Zp∗。
- 计算 c 1 = g r m o d p , c 2 = y r m m o d p c_1=g^r\ mod\ p,c_2=y^rm\ mod\ p c1=gr mod p,c2=yrm mod p
- 密文为 c = ( c 1 , c 2 ) c=(c_1,c_2) c=(c1,c2)。
Dec:
-
解密为 m = c 2 c 1 x m o d p m = \frac {c_2} {c_1^x}\ mod\ p m=c1xc2 mod p。
因为 m = c 2 ∗ ( c 1 x ) − 1 = y r m ∗ ( g r x ) − 1 = y r ∗ y − r ∗ m m o d p m= c_2*(c_1^x)^{-1}=y^rm*(g^{rx})^{-1}=y^r*y^{-r}*m\ mod\ p m=c2∗(c1x)−1=yrm∗(grx)−1=yr∗y−r∗m mod p。
代码实现:
import random
import gmpy2
def keyGen():
p = gmpy2.next_prime(2 ** 1024)
g = random.randint(1, p)
x = random.randint(1, p)
y = gmpy2.powmod(g, x, p)
return (y, g, p), x
# pk = (y,g,p)
def Enc(m, pk: tuple):
# 选择随机数
r = random.randint(1, pk[2])
# 计算c1
c1 = gmpy2.powmod(pk[1], r, pk[2])
# 计算c2
c2 = gmpy2.powmod(pk[0], r, pk[2]) * m % pk[2]
return (c1, c2)
# c = (c1,c2), pk = (y,g,p)
def Dec(c: tuple, sk, pk):
# 计算c1的x次方。
y = gmpy2.powmod(c[0], sk, pk[2])
# 计算c1的x次方的逆元。
y_invert = gmpy2.invert(y, pk[2])
m = c[1] * y_invert % pk[2]
return m
pk, sk = keyGen()
m = 504
c = Enc(m, pk)
m1 = Dec(c, sk, pk)
print(c)
print(m1)
3. ElGamal签名
数字签名在网络安全中有着重要应用,包括有身份认证、不可否认性、数据完整性等。
数字签名实现有多种方式,ElGamal签名体制是基于离散对数签名体制的一种。主要包含3个部分,分别为:keyGen,Sign,Verify,即签名参数生成,签名和验证。
keyGen:
- 选定应该大素数 p p p,和 Z p ∗ Z_p^* Zp∗的生成元 g g g。
- 随机选择一个整数 x ∈ Z p ∗ x\in Z_p^* x∈Zp∗,计算 y = g x m o d p y=g^x\ mod\ p y=gx mod p。
- 将 ( y , g , p ) (y,g,p) (y,g,p)作为公钥, x x x作为私钥。
Sign:
- 计算消息m的哈希值 H : M → Z p ∗ H:M\to Z_p^* H:M→Zp∗, hm = H(m)。
- 选择随机数k: k ∈ Z p ∗ k \in Z_p^* k∈Zp∗,计算 r = g k m o d p r = g^k\ mod\ p r=gk mod p。
- 计算 s ≡ ( h m − x r ) k − 1 m o d p − 1 s\equiv (hm-xr)k^{-1}\ mod \ p-1 s≡(hm−xr)k−1 mod p−1。
- 将 ( r , s ) (r,s) (r,s)作为签名。
Verify:
-
通过等式验证签名, y r r s ≡ g h m m o d p y^rr^s \equiv g^{hm} \ mod\ p yrrs≡ghm mod p。
因为 y r r s ≡ g r x g k s ≡ g r x + h m − r x m o d p y^rr^s \equiv g^{rx}g^{ks}\equiv g^{rx+hm-rx}\ mod\ p yrrs≡grxgks≡grx+hm−rx mod p。
代码实现:
import random
import gmpy2
from hashlib import sha256
def keyGen():
p = gmpy2.next_prime(2 ** 1024)
g = random.randint(1, p)
x = random.randint(1, p)
y = gmpy2.powmod(g, x, p)
return (y, g, p), x
# 签名 传入hash(m),pk = (y,g,p)
def sign(hm, sk, pk: tuple):
# 将消息转为字符串,也可修改为传入bytes数组
q = pk[2] - 1 # q = p -1
# k必需与q互素
k = random.randint(3, q)
g, s, t = gmpy2.gcdext(k, q)
while g != 1:
k = random.randint(3, q)
g, s, t = gmpy2.gcdext(k, q)
k_inv = gmpy2.invert(k, q)
r = gmpy2.powmod(pk[1], k, pk[2])
s = (hm - sk * r) * k_inv % q
return (r, s)
# 验证, sigma = (r,s), pk = (y,g,p)
def verify(hm, sigma: tuple, pk: tuple):
# y^r * r^s % p
left = gmpy2.powmod(pk[0], sigma[0], pk[2]) * gmpy2.powmod(sigma[0], sigma[1], pk[2]) % pk[2]
# g^hm % p
right = gmpy2.powmod(pk[1], hm, pk[2])
if left == right:
return True
else:
return False
pk, sk = keyGen()
m = "hello world"
# 使用hash256,输出结果为2^256范围内,p为2^1024故满足条件
hm = sha256(m.encode("utf-8")).hexdigest()
# 转为整数
hm = int(hm, base=16)
sigma = sign(hm, sk, pk)
assert verify(hm, sigma, pk), "签名验证失败"
print("验证成功")