ElGamal加解密算法——椭圆曲线是男的女的折磨

椭圆曲线基本运算的优美实现


ElGamal加解密的基本原理

选择椭圆曲线 E E E,构造一个椭圆群 E p ( a , b ) E_p(a, b) Ep(a,b)
E p ( a , b ) E_p(a,b) Ep(a,b)中挑选生成生成元 G G G,使满足 n ⋅ G = O n·G=O nG=O的最小的n是一个非常大的素数
选择一个小于 n n n的整数 x x x作为私钥,产生公钥 Y = x ⋅ G Y=x·G YxG

加密算法
  • 在区间 [ 1 , n − 1 ] [1,n−1] [1,n1]内选取随机数 k k k,计算 k ⋅ G k·G kG
  • 将明文 m m m加载到点 P m P_m Pm上,计算 P m + k ⋅ Y P_m+k·Y Pm+kY
  • 传送加密数据 { k ⋅ G , P m + k ⋅ Y } \{k·G,P_m+k·Y\} {kGPm+kY}给接收方。
解密算法

接收方使用自己的私钥x计算:
( P m + k ⋅ Y ) − x ⋅ ( k ⋅ G ) = ( P m + k ⋅ Y ) − k ⋅ ( x ⋅ G ) = P m (P_m+k·Y)−x·(k·G) = (P_m+k·Y)−k·(x·G) = P_m (Pm+kY)x(kG)=(Pm+kY)k(xG)=Pm

设计难点

实现椭圆曲线加强的ElGamal算法,最重要的就是实现完善的椭圆曲线基本算法
需要注意 O O O点的定义、负点的定义、椭圆曲线的加法等等

在最初编写程序的时候,加法公式写错了一个符号,导致了长达几个小时的debug

之后就要进行 E p E_p Ep的构造,这一部分的难点就是如何判断某个数是否是模 p p p的平方剩余
这里我在预处理过程中就将模 p p p的所有平方剩余都储存了下来,实现 O ( 1 ) O(1) O(1)的平方剩余判断

由于合适的椭圆曲线及其生成元非常难构造,
在浏览了大量资料之后,我们确定了一组参数: P = 211 , A = 0 , B = − 4 , G ( 2 , 2 ) P = 211,A = 0,B = -4,G(2, 2) P=211A=0B=4G(2,2)
这组参数可以满足加密的基本要求

将明文加载到椭圆曲线的点上,课本上给出了具体的方法:
在这里插入图片描述
但是针对具体的椭圆曲线,k的取值也不一样
在经过反复实验之后,我们发现当k=16时,可以针对0-9的数字都找到对应的椭圆曲线点
再者,我们需要实现加解密任意长度的明文和密文,因此我们对于消息进行逐(十进制)位的加解密

完整代码

算法本身的实现还是比较简单的,只要有正确的椭圆曲线基本算法支持,就能够较顺利的完成

#include<iostream>
#include<cstring>
#define LL long long

using namespace std;

const LL INF = 0x33333333;
const int MAX = 1e4;
const int N = 10;
const int Block = 1;
const int k_to_word = 16;
bool prime[MAX + 1];
LL is_square[MAX + 1];
LL PRM[MAX / 3];
LL A, B, P, K, m, c;
int tot = 0, EpCnt;
string M;  //按照字符串形式输入明文和密文

struct Node {
	LL x, y;
};
Node Ep[MAX + 1];
Node G, Pa, Pm;
Node Cm1, Cm2;

//扩展欧几里得求逆元
//sa+tb=gcd(a,b)
LL s, t;
void exgcd(LL a, LL b) {
	if (!b) {
		s = 1; t = 0;
		return;
	}
	exgcd(b, a % b);
	LL ss = s;
	s = t;
	t = ss - (a / b) * t;
}

LL MultiInv(LL x) {
	exgcd(x, P);
	return (s + P) % P;
}

bool operator ==(const Node& a, const Node& b) {
	return a.x == b.x && a.y == b.y;
}

Node AddInv(const Node& a) {
	Node tmp;
	tmp.x = a.x;
	tmp.y = P - a.y;
	return tmp;
}

//加法的公式不要写错了
Node operator +(const Node& a,const Node& b) {
	if (a == AddInv(b)) return Ep[0];
	if (a == Ep[0]) return b;
	if (b == Ep[0]) return a;
	LL o;
	if (a == b) {
		o = (3 * a.x * a.x + A) % P * MultiInv(2 * a.y % P) % P;   //注意这里很容易爆LL
	}
	else {
		o = (b.y - a.y + P) % P * MultiInv((b.x - a.x + P) % P) % P;
	}
	Node ans;
	ans.x = ((o * o % P - a.x + P) % P - b.x + P) % P;
	ans.y = ((o * (a.x - ans.x + P) % P) % P - a.y + P) % P;
	return ans;
}

Node operator *(int tm, const Node& a) {
	if (tm == 1) return a;
	Node ans;
	ans = a + a;
	for (int i = 3;i <= tm;++i) 
		ans = ans + a;
	return ans;
}

//素数筛
void cal_prime() {
	memset(prime, 1, sizeof(prime));
	for (int i = 2;i <= MAX;++i)
		if (prime[i]) {
			PRM[tot++] = i;   //素数表
			for (int j = 2;j * i <= MAX;++j) prime[i * j] = 0;
		}
}

void init() {
	P = 211;
	A = 0;
	B = -4;
	G.x = 2;
	G.y = 2;

	EpCnt = 0;
	Ep[EpCnt].x = Ep[EpCnt].y = INF;  //O
	cout << "(INF, INF)";

	//平方剩余
	memset(is_square, -1, sizeof(is_square));  
	for (int i = 0;i <= P/2;++i) 
		is_square[(i * i) % P] = i;

	for (int i = 0;i < P;++i) {
		LL j = (((i * i) % P * i % P) + (A * i) % P + B) % P;
		
		//判断j是否是平方剩余
		if (is_square[j] >= 0) {
			j = is_square[j];

			Ep[++EpCnt].x = i;
			Ep[++EpCnt].y = j;
			cout << ", (" << i << ", " << j << ")";
			if (j) {
				Ep[++EpCnt].x = i;
				Ep[++EpCnt].y = P - j;
				cout << ", (" << i << ", " << P-j << ")";
			}
		}	
	}

	//计算G
	cout << "\nG(" << G.x << ", " << G.y << ")\n\n";
}

void e_ElGamal() {
	//将明文加到点上
	for (int i = 1;i < k_to_word;++i) {
		LL x = (k_to_word * m + i) % P;
		LL y = (((x * x) % P * x % P) + (A * x) % P + B) % P;
		if (is_square[y]>=0) {
			Pm.x = x;
			Pm.y = is_square[y];
			break;
		}
	}
	cout << "嵌入明文信息m的点:\n";
	cout << "(" << Pm.x << ", " << Pm.y << ")\n";
	int k = rand() % (P - 2) + 1;
	Cm1 = k * G;
	Cm2 = Pm + k * Pa;
	cout << "密文:\n";
	cout << "{(" << Cm1.x << ", " << Cm1.y << "), (" << Cm2.x << ", " << Cm2.y << ")}\n";
}

void d_ElGamal() {
	Pm = Cm2 + AddInv(K * Cm1);
	cout << "嵌入明文信息m的点:\n";
	cout << "(" << Pm.x << ", " << Pm.y << ")\n";
	m = Pm.x / k_to_word;
	cout << "明文:\n" << m << endl;
}

LL getbit(string s, int l, int r)
{
	LL ans = 0;
	for (int i = l;i <= r;++i) ans = ans * 10 + (s[i] - '0');
	return ans;
}

void encrypt() {
	K = rand() % (P - 2) + 1;
	Pa = K * G;

	cout << "\n为您自动生成的密钥:\n公开钥Pa(" << Pa.x << ", " << Pa.y << ")\n秘密钥K(" << K << ")\n";

	int len = M.length();
	int exlen = Block - len % Block;
	for (int i = 0;i < exlen;i++) M += "0";  //补充字节

	for (int i = 0;i < (int)ceil((double)len / (double)Block);++i) {  //分段加密
		m = getbit(M, i * Block, (i + 1) * Block - 1);
		e_ElGamal();
	}
}

void decrypt() {
	cout << "请输入解密所需的私钥:\nK=";
	cin >> K;
	d_ElGamal();
}

int main()
{
	srand(static_cast<unsigned int>(time(0)));
	int opt;
	do {
		cout << "\n选择操作: 0->生成Ep(a,b)和G, 1->信息加密, 2->信息解密, 3->退出\n";
		cin >> opt;
		switch (opt) {
		case 0:
			cout << "\n自动生成Ep(a,b):\n ";
			init();
			break;
		case 1:
			cout << "\n请输入待加密明文: ";
			cin >> M;
			encrypt();
			break;
		case 2:
			cout << "\n请输入待解密密文{Cm1(x,y), Cm2(x,y)}:\n";
			cout << "Cm1.x=";
			cin >> Cm1.x;
			cout << "Cm1.y=";
			cin >> Cm1.y;
			cout << "Cm2.x=";
			cin >> Cm2.x;
			cout << "Cm2.y=";
			cin >> Cm2.y;
			decrypt();
			break;
		default:
			break;
		}
	} while (opt != 3);
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值