ElGamal加密算法,与耳熟能详的另一个算法RSA,同属于公钥密码体制,也就是非对称加密。差别在于,ElGamal是基于离散对数问题的难解性。此外,ElGamal算法在椭圆曲线上也有广泛的应用。然而,作为初学者,本文不涉及椭圆曲线内容,仅从算法的简单实现上谈一谈想法。
先回顾一下基本算法:
①密钥的生成:
设p是一个大素数,使得求解(Zp* ,×)上的离散对数问题在计算上是困难的。令g∈Zp* 是一个本原根,选择x,1<x<p-1,计算y≡g^x mod p,则{g,y,p}是公开密钥,{g,x,p}是秘密密钥。
②加密:
对于消息m,发送方选择一个秘密的随机数k,1<k<p,计算:C1≡g^k mod p, C2≡m*(y^k) mod p,然后把C1、C2发送给接受方。
③解密:
接受方收到C1、C2后计算C1^(-x)*C2 mod p≡m,即为发送方的秘密消息。
因为(C1^(-x))*C2≡g^(-x*k)*m*g^(x*k)≡m mod p。
一、大素数p的选取
首先,和许多加密算法一样,在密钥生成中要求我们选取一个大素数p,其中一个原因:使得求解离散对数问题在计算上是困难的,这点暂且不论。
随后,在算法的实现过程中,我因疏忽而选取了一个小的素数,即令p=19,需要传输的消息是数字m=36。
在经历一系列操作之后解密得到的消息却是数字17,如下图:
而17正好是36mod19之后的结果,也就是说最后一步操作中已经得到了原消息,只不过36比模数19要大,使36经历模运算变成了17。
改变了几次原消息m,初步验证:
当原消息m大于模数19时,得到的解密结果只是与m模19同余,但并不相等。其实,这从最后一步的解密算法中就可以看出来了,解密结果是与m模p同余的。
最后,如果m<19:
那么显而易见,就可以直接得到原数。
所以,当p是一个大素数时,取到的m大概率小于p,在解密之后得到的结果就直接等于m,这或许也是p取大素数的原因之一。
二、关于分数取模的运算
最终在解密算法中,出现的指数-x意味着存在分数对p取模。
形式如
≡ n mod p,
往往要求的是n,同余意味着两边可以进行相同的乘积运算,变成:
m*n≡1 mod p,
为了便于理解,举具体的数字为例:
已知:
≡ n mod 19
变形:7n≡1 mod 19
目前不知道更好的方法,只是尝试找到整数n来满足式子,如n取到11时,可以作为式子成立的一个结果。至于n的试根,可以交给计算机,在代码中采用遍历的形式。
然而,往往还会遇到其他情况,
一、分子或者分母中有负数
二、分子或者分母值较大,甚至大于模数p
此时,可以化简
≡
mod p,其中a, b为整数且b不为0
例如:
①
≡
≡11 mod 19
②
≡
≡11 mod 19
三、ElGamal加密算法代码
1、测试代码:
#include<iostream>
using namespace std;
//g是模p幂运算的一个本原根
int x=6, y, g=5, p=193, k=11;
int C1, C2;
int m=36, get_m;
int Pow_Mod(int a, int b, int c){
//求解a的b次方模c的结果
//利用模运算性质
int res=a;
for(int i=1;i<b;i++){
res*=a;
res%=c;
}
return res;
}
void KeyProduction(){
y=Pow_Mod(g, x, p);
}
void Encrypt(){
C1=Pow_Mod(g, k, p);
C2=Pow_Mod(y, k, p);
C2=m*C2%p;
}
int Reverse_Pow_Mod(int a, int b, int c){
//求解a的-b次方模c的结果
a=Pow_Mod(a, b, c);//分母是a的b次方,先通过模运算性质降低分母规模
for(int i=1;;i++)
if((1+i*c)%a==0)
return (1+i*c)/a;
}
void Decrypt(){
get_m=Reverse_Pow_Mod(C1, x, p);
get_m=get_m*C2%p;
}
int main()
{
KeyProduction();
Encrypt();
Decrypt();
cout<<"原消息:"<<m<<endl;
cout<<"传送的C1:"<<C1<<endl<<"传送的C2:"<<C2<<endl;
cout<<"解密后的消息:"<<get_m;
return 0;
}
2、ElGamal加密应用:
#include<iostream>
#include<cstring>
using namespace std;
//公钥{g,y,p}, 私钥{g,x,p}
//g是模p幂运算的一个本原根
int x=6, y, g=5, p=193, k=11;
int C1, C2;
char plaintext[]="I am a student.";
int ciphertext[20];
char result[20];
int Pow_Mod(int a, int b, int c){
//求解a的b次方模c的结果
//利用模运算性质
int res=a;
for(int i=1;i<b;i++){
res*=a;
res%=c;
}
return res;
}
void KeyProduction(){
y=Pow_Mod(g, x, p);
}
void Encrypt(char c){
int m=(int)c;
C1=Pow_Mod(g, k, p);
C2=Pow_Mod(y, k, p);
C2=m*C2%p;
}
int Reverse_Pow_Mod(int a, int b, int c){
//求解a的-b次方模c的结果
a=Pow_Mod(a, b, c);//分母是a的b次方,先通过模运算性质降低分母规模
for(int i=1;;i++)
if((1+i*c)%a==0)
return (1+i*c)/a;
}
int Decrypt(){
int res=Reverse_Pow_Mod(C1, x, p);
res=res*C2%p;
return res;
}
int main()
{
cout<<"原消息为:"<<plaintext<<endl;
KeyProduction();
cout<<"公钥为{5,"<<y<<",193},私钥为{5,6,193}"<<endl;
cout<<"C2为:";
for(int i=0;i<strlen(plaintext);i++){
Encrypt(plaintext[i]);
cout<<C2<<" ";
result[i]=(char)Decrypt();
}
cout<<endl;
cout<<"C1为:"<<C1<<endl;
cout<<"解密后的消息为:"<<result;
return 0;
}