【密码学】一文读懂ElGamal

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 上篇文章我们聊到了Diffie-Hellman的密钥交换协议,这次来聊一个和Diffie-Hellman相似的一个加密算法--ElGamal加密算法,该算法同样选择一个素数p和它的一个原根作为公开密钥。

一文读懂ElGamal


[R_MTFQND]3OLV9XW19WTGW.jpg一文读懂ElGamal

上篇文章我们聊到了Diffie-Hellman的密钥交换协议,这次来聊一个和Diffie-Hellman相似的一个加密算法--ElGamal加密算法,该算法同样选择一个素数p和它的一个原根作为公开密钥。


算法描述

由于这是一个非对称密码体系,和RSA一样,对于加密和解密的密钥是不一样的,下面来简单看一下具体的计算过程,同样的,这里面所涉及的数学知识,也不再介绍了。

密钥生成

  • 选择随机整数, 其中
  • 计算:  =
  • 「私钥」: , 「公钥」:

加密算法

这个同样采取了取模的运算,因此和RSA类似,这个对于明文的长度也是有要求的,假设需要加密的信息是M, 其中M满足: , 如果消息内容过长,同样可以采取分组的方案。

  • 选择随机整数k, 其中
  • 计算一次性密钥:
  • 对M按照下面的方法进行加密得到E(M) = ()

解密算法

  • 计算K =
  • M =

这么只有公式,可能是不好理解,下面来看一个具体的例子,采用的有限域就用GF(19), 然后生成元选择10,先来看一下对数表, 后面图中的有关指数的运算就不用每次都算了,相信手头没有计算器算一个幂还是比较费劲的:

image.gif离散对数表

如下图所示, Alice和Bob完成一次加密和解密:

$OJ}BU2$_WOL9%~HKR]0{WP.pngElGamal加密过程


原理分析

先来看一下为什么, 先来回顾一下Diffie-Hellman的密钥交换算法,Alice和Bob想要最终交换密钥K, 在此之前Alice和Bob分别生成了两个随机的整数, , 然后分别计算,  , 最终我们发现, 这样Alice和Bob完成了密钥交换,这里类似,如果令, 整个过程就完全一致了,详细证明过程可以参考之前的Diffie-Hellman的密钥交换协议一文,在这里就不证明了,至于这里为什么用k, 字母不重要蛤。

下面来解释一下为什么可以通过K来恢复明文:

积极攻击

这里采用ElGamal加密的时候要注意一个问题,就是每次随机选择的k一定要不一样,假设采用两个相同的k去加密消息,尽管可能对于消息, 是不同的,但是最终所使用的K是一样的,因此如果攻击者已知, 那么攻击者会轻松的计算出其他的块。

已知:

image.png

可以得到:

image.png

因此,如果已知M1, 那么可以计算得到:

image.png

代码实现

这里简单采用rust实现一下,只实现了算法的主体过程,有关如何去选择一个素数和获得他的原根,这里就不重复实现了,可以参考之前有关素数和离散对数的相关文章。

use num_bigint::{BigUint, RandBigInt};
use num_traits::{zero, one};
pub struct Elgamal {}
pub struct PublicKey {
    alpha: BigUint,
    p: BigUint,
    y: BigUint,
}
pub struct PrivateKey {
    _alpha: BigUint,
    p: BigUint,
    x: BigUint,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ElGamalCiphertext {
    c1: BigUint,
    c2: BigUint,
}
pub fn mod_inv(u: &BigUint, v: &BigUint) -> BigUint {
    let mut q: BigUint;
    let mut t1: BigUint;
    let mut t3: BigUint;
    let (mut u1, mut u3, mut v1, mut v3) =
        (BigUint::from(1u8), u.clone(), BigUint::from(0u8), v.clone());
    let mut iter: i8 = 1;
    while &v3 != &zero() {
        q = &u3 / &v3;
        t3 = &u3 % &v3;
        t1 = &u1 + &q * &v1;
        u1 = v1;
        v1 = t1;
        u3 = v3;
        v3 = t3;
        iter = -iter;
    }
    if &u3 != &one() {
        return zero();
    }
    if iter < 0 {
        return v - u1;
    }
    u1
}
impl Elgamal {
    fn encrypt(m: &BigUint, pk: &PublicKey) -> ElGamalCiphertext {
        let mut rng = rand::thread_rng();
        let k = rng.gen_biguint_range(&BigUint::from(1u8), &(&pk.p - BigUint::from(1u8)));
        let key = pk.y.modpow(&k, &pk.p);
        let c1 = pk.alpha.modpow(&k, &pk.p);
        let c2 = (key * m) % &pk.p;
        ElGamalCiphertext {
            c1,
            c2,
        }
    }
    fn decrypt(ciphertext: &ElGamalCiphertext, sk: &PrivateKey) -> BigUint {
        let key = ciphertext.c1.modpow(&sk.x, &sk.p);
        let m = &ciphertext.c2 * mod_inv(&key, &sk.p) % &sk.p;
        return m;
    }
}
#[cfg(test)]
mod test {
    use num_bigint::BigUint;
    use crate::elgamal::{PublicKey, PrivateKey, Elgamal};
    #[test]
    fn test() {
        let p = BigUint::from(19u8);
        let alpha = BigUint::from(10u8);
        let x = BigUint::from(5u8);
        let y = alpha.modpow(&x, &p);
        let pk = PublicKey {
            p: p.clone(),
            alpha: alpha.clone(),
            y,
        };
        let sk = PrivateKey {
            p: p.clone(),
            _alpha: alpha.clone(),
            x,
        };
        let m = BigUint::from(17u8);
        let ciphertext = Elgamal::encrypt(&m, &pk);
        println!("{:?}", ciphertext);
        let plaintext = Elgamal::decrypt(&ciphertext, &sk);
        println!("{:?}", plaintext);
    }
}


总结

本文所介绍的ElGamal仅仅是基本的原理,在实际的应用中,同样可以采用RSA的Padding方案,在明文消息当中填充一定数量的随机比特,这样基本可以避免掉上面所述的攻击形式,因为随机填充的内容,攻击者猜到的概率就非常低了,或者很难获取到已知明文对应的密文。

相关文章
|
存储 Rust 并行计算
【密码学】一文读懂XTS模式
这篇文章的灵感来源于我偶然翻到的一个某U盘有关磁盘加密的一个介绍(这一篇不是广告蛤), 然后发现这个模式我之前还真没遇到过,因此呢,就学习了一下,就出来了这一篇文章。
4993 0
【密码学】一文读懂XTS模式
|
Rust 算法 Go
【密码学】一文读懂MurMurHash3
本文应该是MurMurHash算法介绍的最后一篇,来一起看一下最新的MurMurHash算法的具体过程,对于最新的算法来说,整个流程和之前的其实也比较相似,这里从维基百科当中找到了伪代码,也就不贴出来Google官方给出的推荐代码了,先来看一下维基百科给出的伪代码,这里只有32位的伪代码。
2086 0
【密码学】一文读懂MurMurHash3
|
Web App开发 Rust 算法
【密码学】一文读懂ChaCha20
好久没写新的加密算法的原理了, 这次所选取的加密算法结构比较简单, 一起来看一下吧。
7458 0
【密码学】一文读懂ChaCha20
|
算法 数据安全/隐私保护
【密码学】一文读懂Whirlpool
首先呢,祝大家今晚节日快乐,Whirlpool是由Vincent Rijmen(高级加密标准的联合创始人)和Paulo S.L.M.Barreto设计的,后者于2000年首次提出了它。
1086 0
【密码学】一文读懂Whirlpool
|
Rust 算法 安全
【密码学】一文读懂MurMurHash2
上次我们聊过了一代的MurMurHash算法,是的,我又来水文章了,今天呢,接着来聊一下二代的MurMurHash算法,二代算法的整体结构实际上和一代算法差不太多,只是对于每一轮数据的处理过程当中的运算有一些差异,算法的来源依然是来自于Google官网给提供的源码,对着源码看的结构,对于这个算法呢,有两个版本,一个是32位的,一个是64位的,对于32位的算法和64位的算法,区别在于两个初始的魔数不同,整体运算过程还是十分相似的。
2146 0
【密码学】一文读懂MurMurHash2
|
Rust 算法 网络安全
【密码学】一文读懂CMAC
介于上一篇文章比较水,然后这个和上一篇也比较相似,CMAC是为了解决DAA当中安全性不足的问题而出现的,这个算法一共有三个密钥,K, K1, K2, 其中K1和K2可以由K导出,接下来就来一起看一下CMAC的具体过程吧,这一篇文章其实也不长。
3618 0
【密码学】一文读懂CMAC
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂XTEA加密
本篇文章,我们来看一下上一次讲过的TEA加密算法的一个升级版XTEA, 相比于TEA, XTEA的安全性显然是更高的,其中的过程要比TEA稍微复杂一点点。
1251 0
【密码学】一文读懂XTEA加密
|
算法 安全 Go
【密码学】一文读懂HKDF
我这又来水一篇文章,来聊一下HKDF(基于HMAC的密钥导出函数)。密钥派生函数是密钥管理的组成部分,他的目标是通过一些初始的数据派生出来密码学安全的随机密钥。
3050 1
【密码学】一文读懂HKDF
|
存储 安全 算法
为什么人人都要懂点密码学
人类进入二十一世纪以来,随着计算机和移动设备的普及高速发展,我们的社会已经高度信息化,为了防止信息被窃取、修改,就需要对信息的存储、传递进行加密处理,而加密就需要使用到加密算法,解密需要使用密码才可以看到原文。
228 1
|
算法 搜索推荐 安全
【密码学】一文读懂CCM
本文简单介绍了CCM模式下的认证和加密机制,实际上这个是AES-CTR模式和CMAC的一个组合,如果理解了前面这两个,本文应该还是比较好理解的。
3358 0
【密码学】一文读懂CCM