写在前面:EIGamal加密算法用到了不少数论知识,在完成算法前自己把涉及到的数论知识大致都复习了一下,否则很可能在网上看见的解答都云里雾里,而且其中有几个坑网上的解答或者老师PPT都没有说明,可能是觉得这都是些约定俗成的东西。
数论知识点
- 原根
原根又称本原元、生成元。大家都喜欢叫后俩名字,容易让人不知所云。
假设一个数g是P的原根,那么g^i mod P的结果两两不同,且有 1<g<P,0<i<P,归根到底就是g^(P-1) = 1 (mod P)当且仅当指数为P-1的时候成立.(这里P是素数)。
简单来说,g^i mod p ≠ g^j mod p (p为素数),其中i≠j且i, j介于1至(p-1)之间,则g为p的原根。
寻找原根方法:
寻找m的原根。设m= 7,则φ(7)等于6。
设a= 2,由于2^3 =8≡1(mod 7) ,26=64≡1(mod7),23≡2^6(mod7) ,所以 2 不是模 7 的一个原根。设a= 3,由于3^1≡3(mod 7),3^2≡2(mod 7),3^3≡6(mod 7),3^4≡4(mod 7),3^5≡5(mod 7),3^6≡1(mod 7),所以 3 是模 7 的一个原根。
补充一点,只需要验证31,32,33,36即可,这样可以简化运算。
代码实现
for(int i=1;i<=p-1;i++) //获取p-1的所有因子
{
if((p-1)%i==0)
a[length++]=i;
}
for(n=1;;n++) //找到大素数q的原根n
{
int x=-1;
for(int i=0;i<length;i++)
{
if(dia(n,a[i],p)==1) //dia函数:返回n^j mod(p)
{
x=a[i];
break;
}
}
if(x==p-1)
break;
}
2. 逆元
逆元比较简单,就是如果未知数x满足x*a≡1(mod p),那么x就是a的逆元,一般记作a^-1
3. 性质
a^2(mod p)等于(a%p)*(a%p) (mod p),这种方法可以用在计算a^k(mod p)中,否则可能a^k过大导致没办法保存
ElGamal密钥生成
随机选择一个大素数p,且要求p-1有大素数因子。再选择一个模p的原根α。
随机选择一个整数d作为密钥,2≤d≤p-2 。
计算y=α^d mod p,取y为公钥。
ElGamal加密
对于明文M加密
随机地选取一个整数k,2≤k≤p-2。
C1=α^k mod p; C2=MY^k mod p;
其中要注意C2的计算,C2=(M* (Y^k mod p) ) mod p;
密文为(C1,C2)
ElGamal解密
由密文可得明文M
M=C2/C1^d mod p
其中注意M=(C2*(C1^d)-1) mod p
这里要注意的是 ^ 符号永远是最高级的运算符,否则很容易在网上看见公式就直接先计算前面再高次求余。
最终实现代码:
#include <iostream>
#include <cmath>
using namespace std;
int p,n,a[500],d,y;
bool IsPrime(int n)
{
if(n==1)
return false;
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0)
return false;
}
return true;
}
int dia(int n,int j,int q) //返回n^j mod(q) (是数论中判断原根用到的证明方法
{
long long int x=n;
for(int i=1;i<j;i++)
x=((x%q)*(n%q))%q;
x=x%q;
return x;
}
int inv(int n,int j,int q)
{
int x=dia(n,j,q);
for(int i=1;;i++)
{
if((i*x)%q==1)
return i;
}
}
int main()
{
cout<<"输入一个大素数"<<endl;
bool b=false;
do{
cin>>p;
b=IsPrime(p);
if(!b)
cout<<"请保证输入为素数"<<endl;
}while(!b);
int length=0;
for(int i=1;i<=p-1;i++) //获取p-1的所有因子
{
if((p-1)%i==0)
a[length++]=i;
}
for(n=1;;n++) //找到大素数q的原根n
{
int x=-1;
for(int i=0;i<length;i++)
{
if(dia(n,a[i],p)==1) //dia函数:返回n^j mod(p)
{
x=a[i];
break;
}
}
if(x==p-1)
break;
}
cout<<"请输入密钥d的值(2<=d<=p-2)"<<endl;
do{
cin>>d;
b=(d>1&&d<p-1);
if(!b)
cout<<"请保证输入d符合要求"<<endl;
}while(!b);
y=dia(n,d,p);
/*以下为加密过程*/
int k;
cout<<"请输入随机数k:"<<endl;
cin>>k;
cout<<"请输入明文"<<endl;
int text,cipher1,cipher2;
cin>>text;
cipher1=dia(n,k,p);
cipher2=(dia(y,k,p)*text)%p;
cout<<"加密后密文为:("<<cipher1<<','<<cipher2<<")"<<endl;
cout<<"请问是否需要解密?y/n"<<endl;
char ch; cin>>ch;
if(ch=='y'||ch=='Y')
{
cout<<"请输入密文"<<endl;
/*以下为解密过程*/
cin>>cipher1>>cipher2;
text=(cipher2*inv(cipher1,d,p))%p;
cout<<"解密后明文为:"<<text<<endl;
}
return 0;
}