【密码学】一文读懂ElGamal

简介: 上篇文章我们聊到了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 算法 网络安全
【密码学】一文读懂CMAC
介于上一篇文章比较水,然后这个和上一篇也比较相似,CMAC是为了解决DAA当中安全性不足的问题而出现的,这个算法一共有三个密钥,K, K1, K2, 其中K1和K2可以由K导出,接下来就来一起看一下CMAC的具体过程吧,这一篇文章其实也不长。
4996 0
【密码学】一文读懂CMAC
|
存储 算法 安全
【密码学】非对称加密算法 - ECDH
由于 ECC 密钥具有很短的长度,所以运算速度比较快。到目前为止,对于 ECC 进行逆操作还是很难的,数学上证明不可破解,ECC 算法的优势就是性能和安全性高。实际应用可以结合其他的公开密钥算法形成更快、更安全的公开密钥算法,比如结合 DH 密钥形成 ECDH 密钥协商算法,结合数字签名 DSA 算法组成 ECDSA 数字签名算法。ECDH算法常常用来进行密钥的协商,协商好密钥后,用来解决上面的密钥分配问题,将对称加密的密钥安全的传到对端设备。算法加密/解密数字签名密钥交换RSA✅✅✅❌。
4883 0
|
机器学习/深度学习 算法 安全
密码学系列之五:MD5、SHA1——一文搞懂哈希函数
密码学系列之五:MD5、SHA1——一文搞懂哈希函数
9769 0
|
数据库
【latex】在Overleaf的IEEE会议模板中,快速插入参考文献
【latex】在Overleaf的IEEE会议模板中,快速插入参考文献
3343 1
|
机器学习/深度学习 算法 数据挖掘
使用高斯混合模型拆分多模态分布
本文介绍如何使用高斯混合模型将一维多模态分布拆分为多个分布。
271 3
|
网络协议 Unix Linux
十几个免费好用的抓包工具
十几个免费好用的抓包工具
|
人工智能 机器人 Go
飞书+ChatGPT搭建智能AI助手,无公网ip实现公网访问飞书聊天界面
飞书+ChatGPT搭建智能AI助手,无公网ip实现公网访问飞书聊天界面
976 0
|
编译器 Python Windows
解决jupyter以及windows系统中pycharm编译器画图的中文乱码问题大全
解决jupyter以及windows系统中pycharm编译器画图的中文乱码问题大全,我们在jupyter的notebook中使用matplotlib画图的时候,经常性的会遇见一些中文乱码显示□的情况,如下所示:
1099 0
解决jupyter以及windows系统中pycharm编译器画图的中文乱码问题大全
|
人工智能 API 开发者
阿里云CTO周靖人:通义开源模型下载量破2000万,百炼实现150%增长!
阿里云CTO周靖人:通义开源模型下载量破2000万,百炼实现150%增长!
1267 1
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
266 0