ElGamal公鑰密碼算法是在密碼協議中有着重要應用的一類公鑰密碼算法,其安全性是基於有限域上離散對數學問題的難解性。它至今仍是一個安全性良好的公鑰密碼算法。它既可用於加密又可用於數字簽名的公鑰密碼體制。
一、ElGamal公鑰密碼算法描述
1. 選取一個大素數p,使離散對數問題在有限域GF(p)上是難解的,選取g∈Z是一個本原元。
2. 隨機選取整數x,1≤x≤p-2,計算y=g^x(mod p); y是公開的加密密鑰,而x是保密的脫密密鑰。
3. 明文空間爲Z,密文空間爲Z×Z。
4. 加密變換:對任意明文m∈Z,祕密地隨機選取一個整數k,1≤k≤p-2,於是可得密文爲:
c=(c1,c2)
其中
c1=g^k(mod p) , c2=my^k(mod p)
5. 脫密變換:對任意密文c=(c1,c2)∈Z×Z,明文爲:
m=c2×(c1^x)^-1(mod p)
證明:
c2×(c1^x)^-1(mod p)=my^k(g^(kx))^-1 (mod p)
=mg^kx × g^(-kx) (mod p)=m (mod p)
二、ElGamal數字簽名方案
1. 生成乘法羣Z中的一個生成元g,p,g公開。
2. 隨機選取整數x,1≤x≤p-2,計算y=g^x(mod p),y是公開密鑰,而x是保密密鑰。
3. 簽名算法:設m∈Z是待簽名的消息,祕密隨機選取一個整數k,1≤k≤p-2,且(k,p-1)=1,計算
r=g^k(mod p)
s=k^-1(m-rx)(mod p-1)
則(m,r,s)爲對消息m的數字簽名。
4. 驗證算法:對方收到對消息m的數字簽名(m,r,s)後,利用簽名者的公開密鑰y,g,p可對簽名進行以下驗證:
(y^r)(r^s)=g^m(mod p)
如果上式成立,則接受該簽名,否則拒絕該簽名。
對m正確簽名,那麼有:
(y^r)(r^s)(mod p)=g^(rx+sk)(mod p)
=g^(rx+m-rx)(mod p)
=g^m (mod p)
三、ElGamal數字簽名方案實現
爲簡化問題,我們取p=19,g=2,私鑰x=9,則公鑰y=29 mod 19=18。C++代碼實現如下:
BigInt.h
#ifndef BIGINT_H_INCLUDED
#define BIGINT_H_INCLUDED
#include <iostream>
#include <string>
#include <cstdlib>
#include <algorithm>//reverse函數所需添加的頭文件
using namespace std;
/*
大整數類
*/
class BigInt
{
private:
inline int compare(string s1, string s2)
{
if(s1.size() < s2.size())
return -1;
else if(s1.size() > s2.size())
return 1;
else
return s1.compare(s2);
}
public:
bool flag;//true表示正數,false表示負數,0默認爲正數
string values;//保存所有位上的數字
BigInt():values("0"),flag(true){};//構造函數
BigInt(string str)//類型轉換構造函數(默認爲正整數)
{
values = str;
flag = true;
}
public:
friend ostream& operator << (ostream& os, const BigInt& bigInt);//重載輸出操作符
friend istream& operator>>(istream& is, BigInt& bigInt);//輸入操作符重載
BigInt operator+(const BigInt& rhs);//加法操作重載
BigInt operator-(const BigInt& rhs);//減法操作重載
BigInt operator*(const BigInt& rhs);//乘法操作重載
BigInt operator/(const BigInt& rhs);//除法操作重載
BigInt operator%(const BigInt& rhs);//求餘操作重載
};
/*
重載流提取運算符'>>',輸出一個整數
*/
ostream& operator << (ostream& os, const BigInt& bigInt)
{
if (!bigInt.flag)
{
os << '-';
}
os << bigInt.values;
return os;
}
/*
重載流插入運算符'>>',輸入一個正整數
*/
istream& operator >> (istream& is, BigInt& bigInt)
{
string str;
is >> str;
bigInt.values = str;
bigInt.flag = true;
return is;
}
/*
兩個正整數相加
*/
BigInt BigInt::operator+(const BigInt& rhs)
{
BigInt ret;
ret.flag = true;//正整數相加恆爲正數
string lvalues(values), rvalues(rhs.values);
//處理特殊情況
if (lvalues == "0")
{
ret.values = rvalues;
return ret;
}
if (rvalues == "0")
{
ret.values = lvalues;
return ret;
}
//調整s1與s2的長度
unsigned int i, lsize, rsize;
lsize = lvalues.size();
rsize = rvalues.size();
if (lsize < rsize)
{
for (i = 0; i < rsize - lsize; i++)//在lvalues左邊補零
{
lvalues = "0" + lvalues;
}
}
else
{
for (i = 0; i < lsize - rsize; i++)//在rvalues左邊補零
{
rvalues = "0" + rvalues;
}
}
//處理本質情況
int n1, n2;
n2 = 0;
lsize = lvalues.size();
string res = "";
reverse(lvalues.begin(), lvalues.end());//顛倒字符串,以方便從低位算起計算
reverse(rvalues.begin(), rvalues.end());
for (i = 0; i < lsize; i++)
{
n1 = (lvalues[i] - '0' + rvalues[i] - '0' + n2) % 10;//n1代表當前位的值
n2 = (lvalues[i] - '0' + rvalues[i] - '0' + n2) / 10;//n2代表進位
res = res + char(n1 + '0');
}
if (n2 == 1)
{
res = res + "1";
}
reverse(res.begin(), res.end());
ret.values = res;
return ret;
}
/*
兩個正整數相減
*/
BigInt BigInt::operator-(const BigInt& rhs)
{
BigInt ret;
string lvalues(values), rvalues(rhs.values);
//負數減負數
if(flag==false&&rhs.flag==false)
{
string tmp = lvalues;
lvalues = rvalues;
rvalues = tmp;
}
//負數減正數
if(flag==false&&rhs.flag==true)
{
BigInt res(lvalues);
ret=res+rhs;
ret.flag = false;
return ret;
}
if(flag==true&&rhs.flag==false)
{
BigInt rel(lvalues),res(rhs.values);
ret=rel+res;
ret.flag = true;
return ret;
}
//處理特殊情況
if (rvalues == "0")
{
ret.values = lvalues;
ret.flag = true;
return ret;
}
if (lvalues == "0")
{
ret.values = rvalues;
ret.flag = false;
return ret;
}
//調整s1與s2的長度
unsigned int i, lsize, rsize;
lsize = lvalues.size();
rsize = rvalues.size();
if (lsize < rsize)
{
for (i = 0; i < rsize - lsize; i++)//在lvalues左邊補零
{
lvalues = "0" + lvalues;
}
}
else
{
for (i = 0; i < lsize - rsize; i++)//在rvalues左邊補零
{
rvalues = "0" + rvalues;
}
}
//調整使‘-’號前邊的數大於後邊的數
int t = lvalues.compare(rvalues);//相等返回0,str1<str2返回負數,str1>str2返回正數
if (t < 0)
{
ret.flag = false;
string tmp = lvalues;
lvalues = rvalues;
rvalues = tmp;
}
else if (t == 0)
{
ret.values = "0";
ret.flag = true;
return ret;
}
else
{
ret.flag = true;
}
//處理本質情況
unsigned int j;
lsize = lvalues.size();
string res = "";
reverse(lvalues.begin(), lvalues.end());//顛倒字符串,以方便從低位算起計算
reverse(rvalues.begin(), rvalues.end());
for (i = 0; i < lsize; i++)
{
if (lvalues[i] < rvalues[i])//不足,向前借一維
{
j = 1;
while(lvalues[i+j] == '0')
{
lvalues[i+j] = '9';
j++;
}
lvalues[i+j] -= 1;
res = res + char(lvalues[i] + ':' - rvalues[i]);
}
else
{
res = res + char(lvalues[i] - rvalues[i] + '0');
}
}
reverse(res.begin(), res.end());
res.erase(0, res.find_first_not_of('0'));//去掉前導零
ret.values = res;
return ret;
}
/*
兩個正整數相乘
*/
BigInt BigInt::operator*(const BigInt& rhs)
{
BigInt ret;
string lvalues(values), rvalues(rhs.values);
//處理0或結果正負
if (lvalues == "0" || rvalues == "0")
{
ret.values = "0";
ret.flag = true;
return ret;
}
if(flag==false||rhs.flag==false)
{
ret.flag=false;
}
//處理特殊情況
unsigned int lsize, rsize;
lsize = lvalues.size();
rsize = rvalues.size();
string temp;
BigInt res, itemp;
//讓lvalues的長度最長
if (lvalues < rvalues)
{
temp = lvalues;
lvalues = rvalues;
rvalues = temp;
lsize = lvalues.size();
rsize = rvalues.size();
}
//處理本質情況
int i, j, n1, n2, n3, t;
reverse(lvalues.begin(), lvalues.end());//顛倒字符串
reverse(rvalues.begin(), rvalues.end());
for (i = 0; i < rsize; i++)
{
temp = "";
n1 = n2 = n3 = 0;
for (j = 0; j < i; j++)
{
temp = temp + "0";
}
n3 = rvalues[i] - '0';
for (j = 0; j < lsize; j++)
{
t = (n3*(lvalues[j] - '0') + n2);
n1 = t % 10;//n1記錄當前位置的值
n2 = t / 10;//n2記錄進位的值
temp = temp + char(n1 + '0');
}
if (n2)
{
temp = temp + char(n2 + '0');
}
reverse(temp.begin(), temp.end());
itemp.values = temp;
res = res + itemp;
}
ret.values = res.values;
return ret;
}
/*
兩個正整數相除
*/
BigInt BigInt::operator/(const BigInt& rhs)
{
BigInt ret;
string lvalues(values), rvalues(rhs.values);
string quotient;
string temp;
//處理特殊情況
if(rvalues == "0")
{
ret.values = "error";//輸出錯誤
ret.flag = true;
return ret;
}
if(lvalues == "0")
{
ret.values = "0";
ret.flag = true;
return ret;
}
if(compare(lvalues, rvalues) < 0)
{
ret.values = "0";
ret.flag = true;
return ret;
}
else if(compare(lvalues, rvalues) == 0)
{
ret.values = "1";
ret.flag = true;
return ret;
}
else
{
//處理本質情況
unsigned int lsize, rsize;
lsize = lvalues.size();
rsize = rvalues.size();
int i;
if(rsize > 1) temp.append(lvalues, 0, rsize-1);
for(i = rsize - 1; i < lsize; i++)
{
temp = temp + lvalues[i];
//試商
for(char c = '9'; c >= '0'; c--)
{
BigInt t = (BigInt)rvalues * (BigInt)string(1, c);
BigInt s = (BigInt)temp - t;
if(s.flag == true)
{
temp = s.values;
quotient = quotient + c;
break;
}
}
}
}
//去除前導零
quotient.erase(0, quotient.find_first_not_of('0'));
ret.values = quotient;
ret.flag = true;
return ret;
}
/*
兩個正整數取餘
*/
BigInt BigInt::operator%(const BigInt& rhs)
{
BigInt ret,kj(values),ki(rhs.values);
string lvalues(values), rvalues(rhs.values);
string quotient;
string temp;
//處理特殊情況
if(rvalues == "0")
{
ret.values = "error";//輸出錯誤
ret.flag = true;
return ret;
}
if(lvalues == "0")
{
ret.values = "0";
ret.flag = true;
return ret;
}
if(compare(lvalues, rvalues) < 0)
{
if(flag==false)
{
ret.values=(ki-kj).values;
ret.flag = true;
return ret;
}else{
ret.values = lvalues;
ret.flag = true;
return ret;
}
}
else if(compare(lvalues, rvalues) == 0)
{
ret.values = "0";
ret.flag = true;
return ret;
}
else
{
//處理本質情況
unsigned int lsize, rsize;
lsize = lvalues.size();
rsize = rvalues.size();
int i;
if(rsize > 1) temp.append(lvalues, 0, rsize-1);
for(i = rsize - 1; i < lsize; i++)
{
if(temp=="0"){
temp=lvalues[i];
}else{
temp = temp + lvalues[i];
}
//試商
for(char c = '9'; c >= '0'; c--)
{
BigInt t = (BigInt)rvalues * (BigInt)string(1, c);
BigInt s = (BigInt)temp - t;
if(s.flag == true)
{
//cout<<s.values<<endl;
temp = s.values;
quotient = quotient + c;
break;
}
}
}
}
//去除前導零
quotient.erase(0, quotient.find_first_not_of('0'));
ret.values = temp;
ret.flag = true;
return ret;
}
/*
一個大整數和一個小整數的取餘
int divMod(string ch,int num)
{
int s=0;
for(int i=0;ch[i]!='\0';i++)
s=(s*10+ch[i]-'0')%num;
return s;
}*/
/*
歐幾里德求GCD
*/
BigInt gcd(BigInt a,BigInt b)
{
BigInt stemp;
//cout<<a<<endl;
//cout<<b<<endl;
if((a-b).flag==false)//判斷大小
{
stemp.values=a.values;
a.values=b.values;
b.values=stemp.values;
}
if(b.values=="0") return a;
else return gcd(b,a%b);
}
/*
快速冪
*/
BigInt fast(BigInt a,BigInt b)
{
BigInt aa=a,t("1"),k("2");
// int b2=atoi(b1[lsize-1].c_str());
while(b.values!="0")
{
if((b%k).values!="0")
{
t=t*aa;
}
aa=aa*aa;
b=b/k;
}
return t;
}
/*
快速冪模
*/
BigInt mod_fast(BigInt a,BigInt b,BigInt p)
{
BigInt aa=a,t("1"),k("2");
// int b2=atoi(b1[lsize-1].c_str());
while(b.values!="0")
{
if((b%k).values!="0")
{
t=(t%p)*(aa%p)%p;
}
aa=(aa%p)*(aa%p)%p;
b=b/k;
}
return t%p;
}
/*
擴展歐幾里德實現乘法逆
*/
BigInt extgcd(BigInt a, BigInt b, BigInt& x, BigInt& y)
{
BigInt d(a.values);
if(b.values != "0"){
d = extgcd(b, a % b, y, x);
y = y-(a / b) * x;
// cout<<"a:"<<a<<endl;
// cout<<"b:"<<b<<endl;
// cout<<"x:"<<x<<endl;
// cout<<"y:"<<y<<endl<<endl<<endl;
}else {
x.values = "1";
y.values = "0";
}
return d;
}
BigInt mod_inverse(BigInt a, BigInt m)
{
BigInt x, y;
extgcd(a, m, x, y);
if(x.flag==false)
{
x.flag=true;
x=m-x;
}
return (m + x % m) % m;
}
#endif // BIGINT_H_INCLUDED
Main.cpp
#include <iostream>
#include"BigInt.h"
using namespace std;
int main()
{
/*
公開密鑰y,g,p
*/
BigInt p("19"),g("2"),x("9"),y("18"),a("1");
BigInt k("5"),s,r,m,k1;
BigInt t1,t2;
cout<<"請輸入m:"<<endl;
cin>>m;
r=mod_fast(g,k,p);
k1=mod_inverse(k,p-a);
s=((m-x*r)*k1)%(p-a);
cout<<"r:"<<r<<endl;
cout << "s:"<<s << endl;
cout<<"接下來驗證簽名------->"<<endl;
t1=fast(y,r);
t2=fast(r,s);
cout << "t1:"<<t1<< endl;
cout << "t2:"<<t2<< endl;
if(((t1*t2)%p).values==mod_fast(g,m,p).values)
{
cout<<"簽名成功!"<<endl;
}else{
cout<<"簽名失敗!"<<endl;
}
return 0;
}
四、運行結果