ElGamal加密算法

ElGamal算法是一种常见加密算法, 与Diffie-Hellman算法有密切关联。

该算法安全性依赖于计算有限域上离散对数难题:求解离散对数(目前)是困难的,其逆运算指数运算简单。

算法思路:
假设有2个用户Alice 和 Bob,Bob欲使用ElGamal加密算法向Alice发送信息。
对于Alice,首先要选择一个素数q, α是素数q的本原根。 [本原根的概念对应模q乘法群(需循环群)中的生成元。]

  • Alice产生一个XA, XA∈(1, q - 1)
  • 计算YA = αXA mod q
  • A的私钥为XA, 公钥为 {q, α, YA}

公钥存在于某个可信公开中心目录,任何用户都可访问

对于Bob, 首先去上述中心目录访问得Alice的公钥 {q, α, YA}
然后将自己欲发送的明文M, (M ∈ [1, q - 1])洗干净备好。

  • 选一个随机整数k, k ∈ [1, q - 1]
  • 计算可解密自己密文的秘钥 PrivateK = (YA)k mod q (即αXA*k mod q)
  • 将M加密成明文对(C1, C2) 其中
    C1 = αk mod q , C2 = PrivateK * M mod q
  • 明文对发送给Alice

Alice收到明文对:

  • PrivateK = (C1)XA mod q(即αk*XA mod q)
  • M = C2 * PrivateK-1 mod q

到这里..发现算法大多就是一些乘法,求幂之类的运算。剩下个关键内容就是如何寻找素数p的本原根,或者说如何找有限域GF(p)中的生成元。
我们在群这个概念里讨论。
p是素数,令Zp = {1, 2, 3, ..., p - 1},因为考虑乘法,去掉了0元素。
2个定理:

  • Euler定理:设P和a是互素的两个整数,则有aφ(p)=1 mod p
  • 拉格朗日定理: 设 G 是有限群, H 是 G 的子群,|H| 整除 |G|

回顾这样2个概念:设G是群, a∈G, 使得等式ak = e成立的最小整数k称为元素a的阶。而群的阶是指该群中的元素个数。值得留意的是,以某个生成元去生成某个子群,该子群的阶就是该元素的阶(当然了)。

因Zp中所有元素与p互素,由欧拉定理,Zp中所有元素的阶不超过p-1,(因为群的阶φ(p)是p-1,而至少有aφ(p)=1 mod p)。
对于Zp中的任一元素,以该元素为生成元形成的一个循环群,设为S(群S的阶在数值上即该元素的阶),根据群的封闭性可知S必然为Zp的子群,根据拉格朗日定理,可知Zp的元素的阶必然是|Zp| (即p-1)的因子。

于是可以得到这样一个结论:若有这样一个元素a,其阶为Ka, Ka是p-1的平凡因子(即因子1 或者因子p-1), 那么a或者是单位元,或者是生成元。 又知Zp的单位元是1,那么根据单位元的唯一性,可知若a非1,则a必为生成元。问题在于,p-1的因子可能很多,我们还不是得一个个去找到阶是p-1的平凡因子的元素?

为此,我们构造一种特殊的素数,使得p-1的因子数量很少。取p - 1 = 2 * Q ,其中p是素数,Q也是素数。 因为Q是素数,因子仅1, Q。所以p - 1的因子只有 {1, 2, Q, p - 1}四个。
到此已经非常明朗,我们找到满足上述条件的素数p,然后在Zp中寻找这样一个元素a,a的阶非2,非Q,即a^2 mod p != 1 && a^Q mod p != 1,若a又非单位元1,那么a必然是生成元。

留意Zp未必一定有生成元, 若1 到 (p - 1)经上述检验都不满足, 考虑另取一个素数p。至于代码实现上出现的问题:若mpz_probab_prime_p(tmp.mt, 6) == 1 改为 mpz_probab_prime_p(tmp.mt, 6) == 2,p一旦较大,程序运行速度很慢。取2为真素数检验,速度很慢,1为概率素数检验,速度快。

代码实现

#include<bits/stdc++.h>
#include<gmp.h>
#include<gmpxx.h>
using namespace std;

#define mt get_mpz_t()
typedef mpz_class bn;

gmp_randstate_t rstate;

struct public_keys {
    // Big prime p, primitive root, Y = XA^(primitive root)
    bn p, pr, Y; 
    public_keys(bn _p = 0, bn _pr = 0, bn _Y = 0) : p(_p), pr(_pr), Y(_Y) {}
};

// Trusted third party  一个所有用户可访问的公开中心目录
map<string, public_keys> ttp;    

// 返回start 到 end的一个随机数
inline bn randNum(bn start, bn end) {
    bn tmp;
    mpz_urandomm(tmp.mt, rstate, bn(end + 1 - start).mt);
    return tmp + start;
}
// 返回 a^b mod n
inline bn aebmodn(bn a, bn b, bn n) {
    bn ret;
    mpz_powm(ret.mt, a.mt, b.mt, n.mt);
    return ret;
}
// 在2^bits范围内生成一副公钥,存于公开目录中,记在name名下 返回私钥
bn elgmal_keyGen(string name, unsigned long int bits) {
    bn p = 2, pr = -1, Y = -1, bounds = 2;
    mpz_pow_ui(bounds.mt, bounds.mt, bits);

    bool found = 0;
    while(1) {
        mpz_urandomm(p.mt, rstate, bounds.mt);
        mpz_nextprime(p.mt, p.mt);
        bn tmp = (p - 1) / 2;

        if (p < bounds && mpz_probab_prime_p(tmp.mt, 6) == 1) {
            // pr是1到p-1的一个数
            pr = randNum(1, p - 1);
            while (1) {
                // pr^tmp % p
                bn pexpn = aebmodn(pr, tmp, p);
                if (pr != 1 && (pr * pr) % p != 1 && pexpn != 1) {
                    found = 1;
                    break;
                }
                pr = (pr + 1) % p;
            }
        }
        if (found) break;
    }

    bn XA = randNum(2, p - 2);
    Y = aebmodn(pr, XA, p);
    ttp[name] = public_keys(p, pr, Y);
    return XA;
}

// 密文对(C1, C2)
struct cipher_text {
    bn c1, c2;
    cipher_text(bn _c1 = 0, bn _c2 = 0) : c1(_c1), c2(_c2) {}
};

/*
 * 根据公开中心目录中name名下的公钥 对明文m进行加密
 * 返回密文对(C1, C2)
 */
cipher_text elgamal_encrypt(string name, bn m) {
    public_keys pk = ttp[name];
    bn k = randNum(1, pk.p - 1);
    bn privateKey;
    mpz_powm(privateKey.mt, pk.Y.mt, k.mt, pk.p.mt);
    bn cipher1 = aebmodn(pk.pr, k, pk.p);
    bn cipher2 = (m * privateKey) % pk.p;
    return cipher_text(cipher1, cipher2);
}

// 根据在自己name名下的公钥及 自己的秘钥dk 解密密文对ct 返回明文
bn elgamal_decrypt(string name, bn dk, cipher_text ct) {
    public_keys pk = ttp[name];
    bn privateKey = aebmodn(ct.c1, dk, pk.p);
    bn k_inverse;
    mpz_invert(k_inverse.mt, privateKey.mt, pk.p.mt);
    return (ct.c2 * k_inverse) % pk.p;
}

int main() {
    gmp_randinit_mt(rstate);
    gmp_randseed_ui(rstate, time(NULL));
    // Alice初始化自己在中心的公钥并得到自己的私钥
    bn dk = elgmal_keyGen("Alice", 128);
    cout<<"Input the message: ";
    bn n; cin>>n;
    if (n > ttp["Alice"].p) cout<<"message's length is out of bounds\n"; 
    // Bob根据Alice用户信息对明文n加密
    cipher_text ct = elgamal_encrypt("Alice", n);
    // Alice进行解密
    bn res = elgamal_decrypt("Alice", dk, ct);
    cout<<"decrypt   result : " <<res <<"\n";
    return 0;
}                             
示例
» ./ElGamal 
Input the message: 1024
decrypt   result : 1024

注:参考了这篇博客和LJF同学的讨论..有不对请指正..

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • MD5的全称是Message-Digest Algorithm 5,在90年代初由MIT的计算机科学实验室和RSA...
    没能唱给你的歌曲阅读 896评论 2 6
  • 终端之间信息传递安全性的保证始终是业务的刚性需求。不同的加密算法针对不同的业务需求,因为公司是金融公司性质,又不是...
    语歌阅读 2,712评论 0 5
  • 一、SSL协商 由于非对称加密的速度比较慢,所以它一般用于密钥交换,双方通过公钥算法协商出一份密钥,然后通过对称加...
    自我陶醉阅读 2,535评论 1 1
  • RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名。RSA以它的三个发明者Ron Rivest...
    暗物质阅读 1,481评论 0 0
  • 姓名:于川皓 学号:16140210089 转载自:https://baike.baidu.com/item/RS...
    道无涯_cc76阅读 2,460评论 0 1