現代密碼學實驗7 橢圓加密算法ECC的實現

讚賞碼 & 聯繫方式 & 個人閒話

【實驗名稱】ECC算法

 

【實驗目的】

1、掌握密碼學中常用的公鑰密碼算法ECC的算法原理;

2、掌握ECC的算法流程和實現方法。

 

【實驗原理】

橢圓加密算法(ECC)是一種公鑰加密體制,最初由Koblitz和Miller兩人於1985年提出,其數學基礎是利用橢圓曲線上的有理點構成Abel加法羣上橢圓離散對數的計算困難性。

ECC的主要優勢是在某些情況下它比其他的方法使用更小的密鑰,比如RSA加密算法,提供相當的或更高等級的安全。ECC的另一個優勢是可以定義羣之間的雙線性映射,基於Weil對或是Tate對;雙線性映射已經在密碼學中發現了大量的應用,例如基於身份的加密。不過一個缺點是加密和解密操作的實現比其他機制花費的時間長。

 

【實驗內容】

實驗內容: 完成數據的加密運算和解密運算,輸入明文:security,輸入密鑰:cryption 對ASCII碼進行加密和解密。

代碼:(代碼解釋見代碼中註釋)

#include<iostream>
#include<math.h>
#include<time.h>
using namespace std;

class point
{
public:
	int x;
	int y;
};
point P[100];
int num = 0;

//取模
int my_mod(int a, int p)
{
	//注意負數情況要加上一個p
	int i;
	i = a / p;
	int re = a - i * p;
	if (re >= 0)
	{
		return re;
	}
	else
	{
		return re + p;
	}
}

//冪次運算,含模運算,防止溢出
int my_pow(int a, int m, int p)
{
	int result = 1;
	for (int i = 0; i < m; i++)
	{
		result = (result*a) % p;
	}
	return result;
}

//用於求y,並判斷平方根是否爲整數 
int my_sqrt(int s)
{
	int t;
	t = (int)sqrt(s);
	if (t*t == s)
	{
		return t;
	}
	else {
		return -1;
	}
}

void all_points(int a,int b,int p)
{
	for (int i = 0; i < p; i++)
	{
		int s = i * i * i + a * i + b;
		while (s < 0)
		{
			s += p;
		}
		s = my_mod(s, p);
		//判斷是否爲平方剩餘
		//p爲23,是奇素數
		//Euler準則
		int re = my_pow(s, (p - 1) / 2, p);
		if (re == 1)
		{
			//求y
			int n = 1, y;
			int f = my_sqrt(s);
			if (f != -1)
			{
				y = f;
			}
			else 
			{
				for (; n <= p - 1;)
				{
					s = s + n * p;
					f = my_sqrt(s);
					if (f != -1)
					{
						y = f;
						break;
					}
					n++;
				}
			}
			y = my_mod(y, p);
			P[num].x = i;
			P[num].y = y;
			num++;
			if (y != 0)
			{
				P[num].x = i;
				P[num].y = (p - y) % p;
				num++;
			}
		}
	}		
}

void show()
{
	for (int i = 0; i < num; i++)
	{
		cout << P[i].x << " " << P[i].y << endl;
	}
}


//擴展歐幾里得法,遞歸法
int extend(int a, int b, int&x, int&y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	int r = extend(b, a % b, x, y);
	int t = x;
	x = y;
	y = t - a / b * y;
	return r;
}

//藉助遞歸擴展歐幾里得求逆
int inv(int a, int b)
{
	int x, y;
	int r = extend(a, b, x, y);
	if (r != 1)
	{
		return 0;
	}
	x = x % b;
	if (x < 0)
	{
		x = x + b;
	}
	return x;
}


//兩點的加法運算 
point add(point p1, point p2, int a, int p)
{
	long t;int flag = 0;
	int x1 = p1.x;int y1 = p1.y;
	int x2 = p2.x;int y2 = p2.y;
	int tx, ty;int x3, y3;
	
	if ((x2 == x1) && (y2 == y1))
	{
		//相同點
		if (y1 == 0)
		{
			flag = 1;
		}
		else 
		{
			t = (3 * x1*x1 + a)*inv(2 * y1, p) % p;
		}
	}
	else 
	{
		//不同點相加
		ty = y2 - y1;
		tx = x2 - x1;
		while (tx<0)
		{
			tx = tx + p;
		}
		while (ty<0)
		{
			ty = ty + p;
		}
		
		if (tx == 0 && ty != 0)
		{
			flag = 1;
		}
		else 
		{
			//點不相等
			t = ty * inv(tx, p) % p;
		}
	}
	
	if (flag == 1)
	{
		//無限點
		p2.x = -1;
		p2.y = -1;
	}
	else 
	{
		x3 = (t*t - x1 - x2) % p;
		y3 = (t*(x1 - x3) - y1) % p;
		while (x3<0)
		{
			x3 += p;
		}
		while (y3<0)
		{
			y3 += p;
		}
		p2.x = x3;
		p2.y = y3;
	}
	return p2;
}

//隨機選取一個生成元並計算階
int jie(point &pp, int a, int p)
{
	int ii = rand() % num;
	point P0 = P[ii];
	point p1, p2;
	int number = 1;
	p1.x = P0.x; p2.x = P0.x;
	p1.y = P0.y; p2.y = P0.y;
	while (true)
	{
		p2 = add(p1, p2, a, p);
		if (p2.x == -1 && p2.y == -1)
		{
			break;
		}
		number++;
		if (p2.x == p1.x)
		{
			break;
		}
	}
	pp.x = p1.x;
	pp.y = p1.y;
	int n = ++number;
	return n;
}

//素數判斷
bool judge(int num)
{
	bool ret = true;
	int ubound = sqrt(num) + 1;
	for (int i = 2; i < ubound; i++)
	{
		if (num % i == 0)
		{
			ret = false;
			break;
		}
	}
	return ret;
}

//計算kG
point cal(point G, int k, int a, int p)
{
	point temp = G;
	for (int i = 0; i < k - 1; i++)
	{
		temp = add(temp, G, a, p);
	}
	return temp;
}

int main()
{
	srand(time(NULL));
	int a, b, p;
	point generator; int n;
	char SE[10];
	char CR[10];

	cout << "請輸入橢圓曲線羣(a,b,p):";
	cin >> a >> b >> p;
	cout << "請輸入明文:";
	cin >> SE;
	cout << "請輸入密鑰:";
	cin >> CR;

	//計算所有點
	all_points(a, b, p);
	//選取生成元,直到階爲素數
	do
	{
		n = jie(generator, a, p);
	} while (judge(n) == false);
	cout << endl << "選取生成元(" << generator.x << "," << generator.y << "),階爲:" << n << endl;
	//選取私鑰
	int ka = int(CR[0]) % (n - 1) + 1;//選取使用的密鑰
	point pa = cal(generator, ka, a, p);//計算公鑰
	cout << "私鑰:" << ka << endl;
	cout << "公鑰:(" << pa.x << "," << pa.y << ")" << endl;

	//加密
	int k = 0;//隨機數k
	k = rand() % (n - 2) + 1;
	point C1 = cal(generator, k, a, p);//計算C1
		
	//m嵌入到橢圓曲線
	int t = rand() % num; //選擇映射點
	point Pt = P[t];
	point P2 = cal(pa, k, a, p);
	point Pm = add(Pt, P2, a, p);
	cout << endl << "要發送的密文:" << endl;
	cout << "kG=(" << C1.x << "," << C1.y << "),pt+kPa=(" << Pm.x << "," << Pm.y << ")";
	int C[100];
	cout<<",C = { ";
	for (int i = 0; i<strlen(SE); i++)
	{
		C[i] = int(SE[i]) * Pt.x + Pt.y;//選取要加密的明文
		cout<< C[i] <<" ";
	}
	cout << "}" << endl;

	
	//解密
	point temp, temp1;
	int m;
	temp = cal(C1, ka, a, p);
	temp.y = 0 - temp.y;
	temp1 = add(Pm, temp, a, p);//求解Pt 
	printf("\n解密結果:\n");
	for (int i = 0; i<strlen(SE); i++)
	{
		m = (C[i] - temp1.y) / temp1.x;
		printf("%c", char(m));//輸出密文 
	}
	printf("\n");

	return 0;
}

運行演示:

 

【小結或討論】

這一次是實驗可以說是很有難度的。雖然這個實驗和之前的ElGamal算法原理相似,但是其主要是引入了橢圓曲線羣。有限域上的橢圓曲線,再加上自己定義的“+”法,其中又涉及到取模、冪次運算、判斷平方根、擴展歐幾里得法、模數求逆、生成元計算階、素數判斷等等內容,可以說是十分複雜了。

我結合着自己的理解,對算法流程和題目要求做了一些修改,以便更好地實現。由於題目給定的p爲23,小了一些,我就不能按照指導書上的流程來完整實現,這不僅是,x取值範圍固定爲0~22的範圍而導致數據無法恢復的問題,還有其可表示的信息太少了,以至於不能表示出26個英文字母的ASCII碼值。爲了解決這個問題,我結合着網上的一些資料,對把信息m嵌入橢圓曲線的過程做了少許修改,具體實現可以參考上文代碼,大概流程就是任意取一個橢圓曲線上的點,C[i] = int(SE[i]) * Pt.x + Pt.y,這樣等於隱藏的是我們選擇的這一點。解密時通過m = (C[i] - temp1.y) / temp1.x,反向計算即可恢復,這也算是一種信息的嵌入方式。此外,由於給的密文和密鑰是英文字符,如果轉換成ASCII碼我擔心計算過程的溢出問題,所以我是拆成一個一個字母處理的,不過這並不影響整個ECC的實現原理和方法。

這一次的實驗我感覺有些的綜合性,用上了我們之前實驗用過的模數求逆、冪次運算、素數判斷等,也加上了平方根判斷、生成元計算計算等新內容。實驗代碼雖然不是歷次實驗最多的,我卻覺得是最爲複雜的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章