EIGamal加解密算法详述

写在前面:EIGamal加密算法用到了不少数论知识,在完成算法前自己把涉及到的数论知识大致都复习了一下,否则很可能在网上看见的解答都云里雾里,而且其中有几个坑网上的解答或者老师PPT都没有说明,可能是觉得这都是些约定俗成的东西。

数论知识点

  1. 原根

原根又称本原元、生成元。大家都喜欢叫后俩名字,容易让人不知所云。

假设一个数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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值