基于ElGamal算法的加密与签名机制

基于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:

  1. 选定应该大素数 p p p,和 Z p ∗ Z_p^* Zp的生成元 g g g
  2. 随机选择一个整数 x ∈ Z p ∗ x\in Z_p^* xZp,计算 y = g x   m o d   p y=g^x\ mod\ p y=gx mod p
  3. ( y , g , p ) (y,g,p) (y,g,p)作为公钥, x x x作为私钥。

Enc:

  1. 设明文消息为m,随机选择 r ∈ Z p ∗ r\in Z_p^* rZp
  2. 计算 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
  3. 密文为 c = ( c 1 , c 2 ) c=(c_1,c_2) c=(c1,c2)

Dec:

  1. 解密为 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=yryrm 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:

  1. 选定应该大素数 p p p,和 Z p ∗ Z_p^* Zp的生成元 g g g
  2. 随机选择一个整数 x ∈ Z p ∗ x\in Z_p^* xZp,计算 y = g x   m o d   p y=g^x\ mod\ p y=gx mod p
  3. ( y , g , p ) (y,g,p) (y,g,p)作为公钥, x x x作为私钥。

Sign:

  1. 计算消息m的哈希值 H : M → Z p ∗ H:M\to Z_p^* H:MZp, hm = H(m)。
  2. 选择随机数k: k ∈ Z p ∗ k \in Z_p^* kZp,计算 r = g k   m o d   p r = g^k\ mod\ p r=gk mod p
  3. 计算 s ≡ ( h m − x r ) k − 1   m o d   p − 1 s\equiv (hm-xr)k^{-1}\ mod \ p-1 s(hmxr)k1 mod p1
  4. ( r , s ) (r,s) (r,s)作为签名。

Verify:

  1. 通过等式验证签名, y r r s ≡ g h m   m o d   p y^rr^s \equiv g^{hm} \ mod\ p yrrsghm 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 yrrsgrxgksgrx+hmrx 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("验证成功")

参考文献

[1] ElGamal T. A public key cryptosystem and a signature scheme based on discrete logarithms[J]. IEEE transactions on information theory, 1985, 31(4): 469-472.
  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

都学点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值