浅谈ElGamal加密算法

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取模。

形式如 \frac{1}{m} ≡ n mod p,

往往要求的是n,同余意味着两边可以进行相同的乘积运算,变成:

m*n≡1 mod p,

为了便于理解,举具体的数字为例:

已知:\frac{1}{7} ≡ n mod 19

变形:7n≡1 mod 19

目前不知道更好的方法,只是尝试找到整数n来满足式子,如n取到11时,可以作为式子成立的一个结果。至于n的试根,可以交给计算机,在代码中采用遍历的形式。

然而,往往还会遇到其他情况,

一、分子或者分母中有负数

二、分子或者分母值较大,甚至大于模数p

此时,可以化简

\frac{a}{b} ≡\frac{a mod p}{b mod p} mod p,其中a, b为整数且b不为0

例如:

\frac{-18}{7}\frac{1}{7}≡11 mod 19

\frac{20}{26}≡ \frac{1}{7} ≡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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值