基于RSA算法的文件加解密系统

一、RSA背景介绍

    RSA算法是一种非对称加密算法,由三位数学家Rivest、Shamir和Adleman在1977年发明。RSA算法的安全性基于数学上的两个大质数相乘很容易,但是将其因数分解却非常困难的特性。RSA算法被广泛应用于数字签名、密钥交换、数据加密等领域,是目前最常用的公钥加密算法之一。

二、RSA算法原理、设计及分析

1.RSA算法

    RSA算法是一种非对称加密算法,所以它会产生公钥与私钥两种密钥,在加密时,用公钥对需要加密的数据进行加密,而解密时,用私钥对需要解密的数据进行解密。

下面是RSA算法加解密的步骤:

(1)先选择两个大质数p和q,然后计算它们的乘积n=p*q。

(2)再选择一个整数e,使得1<e<φ(n),且e与φ(n)互质,其中φ(n)=(p-1)*(q-1)。       

(3)计算e关于φ(n)的模反元素d,即满足e*d≡1(mod φ(n))的整数d。

(4)由此可得到公钥为(e,n),私钥为(d,n)。

(5)加密时,先将明文m转换成整数M,然后用公式C=M^e(mod n)计算,得到的整数C就是密文。 

(6)解密时,先将密文C用公式M=C^d(mod n)计算,得出明文整数M,然后将M转换成明文即可。

根据以上步骤,可以实现数据的加解密操作。

2.RSA算法步骤

(1)密钥产生:

(2)加密:

(3)解密:

 3.素性检测

    根据前面RSA算法的步骤我们可以知道,在进行加解密之前,我们需要先找到两个大质数。对于大质数的寻找,我们可以采用随机数生成和Miller_Rabin素性检测的方法来寻找。对于随机数生成,我们可以以系统时间为种子,利用srand()函数来生成。对得到随机数进行素性检测。判断一个数是否为素数,可以有两种判断方法,一是穷举因子,二是素性检测。

  (1)穷举因子:如果采用穷举因子的方法,对于数n,我们只需要从1开始查找到n,把所有能整除n的数记录下来即可。但此中算法时间复杂度为O(n)。当数较大时,查找会很慢。所以,我们可以采用另一种更高效的方法进行查找。从1查找到n的平方根,因为如果一个数a是n的因子,那么n/a也一定是n的因子。因此,只需要枚举1到sqrt(n),判断哪些数能够整除n即可。此算法时间复杂度为O(sqrt(n)),比之前快上了许多。

 (2)素性检测:判断一个数是否为素数,除了采用素性检测的方法,我们还可以采用Miller-Rabin素性检测。Miller-Rabin素性检测的原理是基于费马小定理和欧拉定理,利用了随机化的思想来提高检测的效率。但是Miller-Rabin素性检测的结果并不是百分百正确,他是采用多次检测,提高结果正确的概率来保证检测的准确性。为了更好的理解素性检测,我们先阐释一下费马小定理,若p是一个质数,a是任意整数且a不是p的倍数,则有a^(p-1) ≡ 1 (mod p)。这个定理满足绝大多数的判断,但是也会出现例外,比如Carmichael数。为了减少出现误判的概率,于是出现了费马小定理的升级版Miller-Rabin素性检测法。当先用费马小定理对某个数进行检测以后,再利用Miller-Rabin素性检测对某个数进行二次探测可大大提高结果的准确性。

以下是素性检测的步骤:

a. 将待检测的数p减去1得到p-1,再将其分解为d*2^s的形式,其中d为奇数。

b. 在大于1小于p-2范围内选择一个随机数a。

c. 先计算a^d mod n,如果结果为1或者n-1,则n可能是素数。

d. 然后使i从1到s-1,遍历计算a^(2^i * d) mod n,如果结果为n-1,则n可能是素数,进入下一轮检测。

e. 如果以上步骤都没有找到n不是素数的证据,则n很可能是素数。

f. 最后重复执行2-5步,直到达到预设的检测次数或者确定n是合数。如果到预设的检测次数依然判断该数为素数,我们基本可以确定该数就是素数。

    以上是对Miller-Rabin素性检测榨干的操作步骤,但是我们在设计程序时更多的考虑是程序的实用性,所以我们可以仅取p-1除2进行计算。取随机数a,然后计算a^((p-1)/2) mod p,如果结果为1或p-1,我们重新取随机数a,再进行判断,当达到预设次数以后,没有出现合数的判定,我们就确定其为素数。

三、程序设计

1.main函数设计

int main()
{
	//--------------------------------------------------------------随机生成+遍历判断素性
	//srand(time(NULL));
	//while (zhishuA < 100 || zhishuA > 999 || !Panduan(zhishuA)) {
	//	zhishuA = rand() % 900 + 100;
	//}
	//while (zhishuB < 100 || zhishuB > 999 || !Panduan(zhishuB)) {
	//	zhishuB = rand() % 900 + 100;
	//}
	//printf("生成的大素数p为:");
	//printf("%d\n", zhishuA);
	//printf("生成的大素数q为:");
	//printf("%d\n", zhishuB);
	//RSA();

	//-----------------------------------------------------------scanf输入程序测试
	//printf("请输入两个素数:");
	//scanf("%d %d", &zhishuA, &zhishuB);//验证
	//RSA();

    //---------------------------------------------------------------Miller_rabin素性检测

	srand(time(NULL));
	int i = 0, n = 0;
	while (1)
	{
		i = rand() % 900 + 100;
		if (feima(i) == 1)
		{
			if (Miller(i) == 1)
			{
				zhishuA = i;
				break;
			}
		}
	}
	printf("生成素数p为:");
	printf("%d\n", zhishuA);

	while (1)
	{
		n = rand() % 900 + 100;
		if (feima(n) == 1)
		{
			if (Miller(n) == 1)
			{
				if (n!=i)
				{
					zhishuB = n;
					break;
				}

			}
		}
	}
	printf("生成素数q为:");
	printf("%d", zhishuB);
	RSA();

}

    在main程序里通过三种方式进行素数选取。第一种,通过rand随机数生成,然后采用穷举因子的方式进行素性判断。第二种直接采用手动输入的方式进行素数输入。第三种采用rand随机数生成加上Miller-Rabin素性检测进行素数选取。该文主要采用第三种素数生成方式进行素数选取,首先以系统时间为种子,生成一个三位的随机数(这里生成三位并不是只能进行三位运算,而是因为四位,五位虽然可以计算,但计算时间较长,为了方便演示所以采用三位计算),然后对其先进行费马小定理一次检测,通过费马小定理检测以后,再用Miller-Rabin素性检测法进行二次探测,当两种探测均通过以后,可判断该随机数为素数,然后将其赋值给zhishuA。同样的方式,再进行第二次,但是由于种子精度有限,短时间内多次进行程序,会导致两次生成的随机数一样,所以在第二次随机数生成中进行了判断,当生成的随机数通过费马小定理与素性检测以后,且与第一次生成的值不一样,再将其赋值给zhishuB。得到两个随机质数以后,便可进行RSA加解密。

2.费马小定理

int feima(int sushu)
{
	srand(time(NULL));

	if (sushu==2)
	{
		return 1;
	}
	if(sushu==1||sushu==0)
	{
		return 0;
	}
	a = rand() % sushu;
	while (a == 0 || a == 1)
	{
		a = rand() % sushu;
	}

	mpz_init_set_ui(shua, sushu);
	mpz_init_set_ui(pd, a);
	mpz_pow_ui(mijg, pd, sushu-1);
	mpz_mod(mojg, mijg, shua);
	mjg = mpz_get_si(mojg);
	//printf("%d", mjg);
	return mjg;
}//费马小定理一次检测

    所示程序为费马小定理一次检测素数。将随机生成的数传进来以后(这里表示随机数的变量为sushu),首先判断他是否为0、1、2中的一个,如果等于2,则它一定是素数,返回1;如果等于1或0,则它一定不是素数,返回0。因为在费马小定理中,随机数a的取值范围是1到sushu-1,所以在程序里,将随机生成的数对sushu取余,得到的值保证小于sushu,再赋值给a,紧接着会对生成的a进行检测,如果为0或1,就再次检测,直到不为0或1为止。然后使用GMP库中的函数对sushu和a进行初始化,并使用mpz_pow_ui函数计算a的sushu-1次方,再使用mpz_mod函数计算a的sushu-1次方对sushu取模的结果。最后,使用mpz_get_si函数将结果转换为整型并返回。整型变量mjg表示a的sushu-1次方对sushu取模的结果。如果为1,则再进行素性检测二次探测。(补充:在这里有提到GMP库,GMP库是专用于大整数计算的库。因为在进行计算时,会出现三位数的几百次幂,最后得到的值会超出long long型,所以采用传统的变量定义会出现数据溢出问题。而一般解决这种问题的方法就是采用字符数组去存储。如果仍然想采用long long型,就可以选用快速幂算法。但是不论采用哪种方法进行起来都比较麻烦,为了方便,所以这里采用了GMP库进行大整数运算。)

3.Miller-Rabin素性检测

int Miller(int msushu)
{
	srand(time(NULL));
	int suiji = 0;
	int jiance = 0;
	int erci = 0;
	int mozhi = 0;

	if (msushu==2||msushu==3)
	{
		return 1;
	}

	erci = (msushu-1)/2;
	mpz_init_set_ui(msu, msushu);
	for ( jiance = 0; jiance < 10; jiance++)
	{
		suiji = rand() % msushu;
		while (suiji == 0 || suiji == 1 || suiji == msushu - 1 )
		{
			suiji = rand() % msushu;
		}
		mpz_init_set_ui(suijishu, suiji);
		mpz_pow_ui(suijimi, suijishu, erci);
		mpz_mod(qmo, suijimi, msu);
		mozhi = mpz_get_si(qmo);
		//printf("%d", mozhi);
		if (mozhi!=1&&mozhi!=msushu-1)
		{
			return 0;
		}
	}
	return 1;
}//Miller_Rabin素性检测二次探测

    该部分是一个 Miller-Rabin 素性检测算法的实现。首先判断传入的数 msushu,如果等于 2 或 3,直接返回 1,因为这两个数都是素数,另外就是为什么这里只检测2和3,却不检测0和1,因为在程序里,素性检测是作为二次探测而进行的,主要是为了对费马小定理检测出的结果进行再判断,而不满足费马小定理的数(包括0和1)在一次检测中就已经被排除,所以这里没有0和1的判断。然后计算(msuhsu-1/2)的值并将其赋值给erci,使用 mpz_init_set_ui 函数初始化一个大整数变量 msu,将 msushu 的值存储到其中。 接着就是循环检测10次,因为在这里随机数取值范围为2到n-2,所以每次循环中,随机生成的数先判断是否等于 0、1 或 msushu-1,如果是,则重新生成随机数,直到出现满足条件的随机数。 使用 mpz_init_set_ui 函数初始化一个大整数变量 suijishu,将 suiji 的值存储到其中。然后使用 mpz_pow_ui 函数计算 suijishu 的 erci 次方,将结果存储到另一个大整数变量 suijimi 中。 使用 mpz_mod 函数计算 suijimi 对 msu 取模的结果,将结果存储到另一个大整数变量 qmo 中。然后使用 mpz_get_si 函数将 qmo 转换为一个普通整数 mozhi。如果 mozhi 不等于 1 且不等于 msushu-1,则说明 msushu 不是素数,直接返回 0。循环结束后,如果没出现不是素数的情况,则说明 msushu 可能是素数,返回 1。

4.RSA加解密

void RSA()//私钥、公钥产生
{
    //long long int miyunsuan = 0;
	long long int zhishuchengji = 0;
	long long int Oulashu = 0, Oulazhishu = 0, Oulaxishu = 0;
	long long int mofanshu = 0;
	int tiaochuxunhuan = 0;
	int choice = 0;
	zhishuchengji = zhishuA * zhishuB;
	Oulashu = (zhishuA - 1) * (zhishuB - 1);
	printf("\n欧拉函数为:%lld\n", Oulashu);
	printf("请输入与欧拉函数互质的整数:");
	scanf("%lld", &Oulazhishu);
	if (gcd(Oulashu, Oulazhishu)!=1)
	{
		printf("输入数与欧拉函数不互质");
		exit(0);
	}
	while (1)
	{
		for ( mofanshu = 0; mofanshu <= Oulashu; mofanshu++)
		{
			if(mofanshu*Oulazhishu==Oulaxishu*Oulashu+1)
			{
				tiaochuxunhuan = 1;
				break;
			}
		}
		if (tiaochuxunhuan==1)
		{
			break;
		}
		Oulaxishu += 1;
	}
	printf("\n模反数:%lld", mofanshu);
	printf("\n欧拉系数:%lld\n", Oulaxishu);
	//printf("%d", mofanshu);
	printf("公钥为(%lld %lld)", Oulazhishu, zhishuchengji);
	printf("私钥为(%lld %lld)", mofanshu, zhishuchengji);
	


	while (1)
	{
		printf("\n请选择加密或解密:");
		scanf("%d", &choice);
		if (choice == 1)
		{
			qOulazhishu = Oulazhishu;
			qmofanshu = mofanshu;
			mpz_init_set_ui(qzhishuchengji, zhishuchengji);
			mpz_init(miyunsuan);
			JmOpenbook();
		}
		else if (choice == 2)
		{
			qOulazhishu = Oulazhishu;
			qmofanshu = mofanshu;
			mpz_init_set_ui(qzhishuchengji, zhishuchengji);
			mpz_init(miyunsuan);
			jmopenbook();
		}
		else if (choice == 3)
		{
			break;
		}
	}
}

    这里是RSA加密步骤,先是根据两个质数求得两数乘积,再求得欧拉函数,然后需要用户输入一个与欧拉函数互质的数,紧接着会用欧几里得辗转相除法,求用户输入的数与欧拉函数的最大公约数,如果不为1,则可判断两数不互质,如果为1,则两数互质。当判断互质以后会进行求模反逆元的运算。注意,这里求模反逆元采用的不是欧几里得扩展算法,而是采用了类似暴力算法的直接查找方式,根据公式e*d≡1(mod φ(n)),可以转换为e*d=k*φ(n)+1。这里已经确定的是e和φ(n),所以可以令k为0,然后让d从0开始进行加1遍历,直到满足e*d=k*φ(n)+1时,可得到d的值,如果出现e*d>k*φ(n)+1时,则令k自加1,再次从0开始对d进行加1遍历,直到满足等式的情况出现。循环进行,直到出现满足的情况,得到d值。当求出模反逆元以后,便可得到私钥和公钥。

5.加密文件读取

void JmOpenbook()
{
	FILE* fp;  FILE* op;
	char ch;
	int num = 0;
	fp = fopen("C:\\Users\\Lenovo\\Desktop\\RSA\\Project1\\明文.txt", "r"); // 打开文件
	op = fopen("C:\\Users\\Lenovo\\Desktop\\RSA\\Project1\\明文加密.txt", "w");
	if (fp == NULL) {
		printf("Error opening file\n");
		exit(1);
	}
	while ((ch = fgetc(fp)) != EOF) { // 逐个读取文件中的字符
		//printf("%x", ch);
		/*fputc(ch, op);*/
		/*fprintf(op, "%c", ch);*/
		jiamiwen = (int)ch;
		printf("明文10进制为:%d\n", jiamiwen);
		
		Jiami();
		//fwrite(&cunru, sizeof(long long int), 1, op); // 写入数据
		
		fprintf(op, "%lld ", cunru);
		/*fputc()*/
	}
	fclose(op); // 关闭文件
	return 0;
}

    这里是加密程序设计,根据ASCLL码表一个一个读取明文文件的数据,然后将每个数据强制转换为十进制,进行加密,将加密的数据存入到明文加密文件。循环进行,直到文件中没有数据为止。

6.加密设计

void Jiami()//加密
{
	//int jiamiwen = 0;
	//printf("\n请输入明文:");
	//scanf("%d", &jiamiwen);
	mpz_init_set_ui(mingwen, jiamiwen);
	mpz_pow_ui(miyunsuan, mingwen, qOulazhishu);
	printf("幂运算结果输出:");
	gmp_printf("%Zd\n", miyunsuan);//幂运算输出测试
	mpz_mod(miyunsuan, miyunsuan, qzhishuchengji);
	printf("加密后密文:");
	gmp_printf("%Zd\n", miyunsuan);
	cunru = mpz_get_si(miyunsuan);
	//cunru = miyunsuan;
	/*printf("%lld", cunru);*/
}

    得到公钥以后,利用公钥对明文进行加密。先求明文的e次方,然后对质数乘积取模,便可得到加密的密文。最后将加密的密文赋值给全局变量cunru中,再转存到文件函数里。因为这里会出现数据溢出,所以全部采用GMP库函数处理。

7.解密文件读取

void jmopenbook()
{
	FILE* dp;  FILE* mp;
	int ch1=0;
	long long int shuju = 0;
	int tiaochu = 0;
	dp = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\明文加密.txt", "r");
	mp = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\解密.txt", "w");
	if (dp == NULL) 
	{
		printf("Error opening file\n");
		exit(0);
	}
	while (1)
	{
		shuju = 0;
		while ((ch1 = fgetc(dp)) != ' ')
		{
			//printf("%x", ch1);
			if (ch1 >= '0' && ch1 <= '9')
			{
				shuju = shuju * 10 + (ch1 - '0');
			}
			else
			{
				tiaochu = 1;
				break;
			}
		}
		/*printf("%d", shuju);*/
		if (tiaochu == 1)
		{
			break;
		}
		jiemiwen = shuju;
		printf("\n密文输出:%lld", jiemiwen);
		Jiemi();
		fprintf(mp, "%c", (char)shuchu);
	}
	fclose(mp);
}

    该部分为解密文件读取,因为加密时,产生的密文长度不相等,所以在加密的时候,将每组密文结尾补上一个空格,当解密文件读取到空格时,则表示一组数据。将读取的一组数据转换为一个整形变量再进行解密。最后将解密到的十进制明文根据ASCLL码表转换成对应的字符,存到文件中。

8.解密函数设计

void Jiemi()//解密
{
	mpz_init_set_ui(miwen, jiemiwen);
	mpz_pow_ui(miyunsuan, miwen, qmofanshu);
	printf("\n幂运算结果输出:");
	gmp_printf("%Zd\n", miyunsuan);//幂运算输出测试
	mpz_mod(miyunsuan, miyunsuan, qzhishuchengji);
	printf("解密后明文:");
	gmp_printf("%Zd", miyunsuan);
	shuchu = mpz_get_si(miyunsuan);
}

    解密函数与加密函数设计方式一样,因为运算的数据长度较长,也采用了GMP库的运算方式。不同的是这里解密采用的是私钥而不是公钥,先求密文的d次方,再进行模运算输出,最后也存到一个全局变量shuchu中,返还到文件函数里。

9.补充--穷举因子判断素性

int main()
{
    srand(time(NULL));
	while (zhishuA < 100 || zhishuA > 999 || !Panduan(zhishuA)) {
		zhishuA = rand() % 900 + 100;
	}
	while (zhishuB < 100 || zhishuB > 999 || !Panduan(zhishuB)) {
		zhishuB = rand() % 900 + 100;
	}
	printf("生成的大素数p为:");
	printf("%d\n", zhishuA);
	printf("生成的大素数q为:");
	printf("%d\n", zhishuB);
	RSA();
}

    这里采用穷举因子的方式进行素性判断,这里是直接生成一个三位随机素数,首先生成三位的随机数,然后用Panduan函数对其进行穷举因子进行素性检测。满足以后,则将其取出赋值。

int Panduan(int n) 
{
	if (n < 2) {
		return 0;
	}
	for (int i = 2; i * i <= n; i++) 
	{
		if (n % i == 0)
		{
			return 0;
		}
	}
	return 1;
}//返回值为1表示是素数,为0则不是

    此处为穷举因子函数,如果返回值为1表示n是素数,如果返回值为0表示n不是素数。 首先,如果n小于2,则直接返回0,因为小于2的数都不是素数。 然后,从2开始循环到sqrt(n),如果n能被i整除,则说明n不是素数,直接返回0。否则,继续循环直到i * i > n,此时如果还没有返回0,则说明n是素数,返回1。另外有一点需注意,这里使用i * i <= n 而不是 i <= sqrt(n) 是为了避免使用浮点数运算,减少不必要的麻烦。还有这里为了提高效率,采用的是遍历到n的平方根,而不是直接遍历到n。

四、程序展示

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<gmp.h>

int jiamiwen = 0;
int zhishuA = 0, zhishuB = 0;
mpz_t mingwen, miwen;
mpz_t miyunsuan, qzhishuchengji;
long long int qOulazhishu = 0, qmofanshu = 0;
long long int cunru = 0;

void Jiami()//加密
{
	//int jiamiwen = 0;
	//printf("\n请输入明文:");
	//scanf("%d", &jiamiwen);
	mpz_init_set_ui(mingwen, jiamiwen);
	mpz_pow_ui(miyunsuan, mingwen, qOulazhishu);
	printf("幂运算结果输出:");
	gmp_printf("%Zd\n", miyunsuan);//幂运算输出测试
	mpz_mod(miyunsuan, miyunsuan, qzhishuchengji);
	printf("加密后密文:");
	gmp_printf("%Zd\n", miyunsuan);
	cunru = mpz_get_si(miyunsuan);
	//cunru = miyunsuan;
	/*printf("%lld", cunru);*/
}


void JmOpenbook()
{
	FILE* fp;  FILE* op;
	char ch;
	int num = 0;
	fp = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\明文.txt", "r"); // 打开文件
	op = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\明文加密.txt", "w");
	if (fp == NULL) {
		printf("Error opening file\n");
		exit(1);
	}
	while ((ch = fgetc(fp)) != EOF) { // 逐个读取文件中的字符
		//printf("%x", ch);
		/*fputc(ch, op);*/
		/*fprintf(op, "%c", ch);*/
		jiamiwen = (int)ch;
		printf("明文10进制为:%d\n", jiamiwen);
		
		Jiami();
		//fwrite(&cunru, sizeof(long long int), 1, op); // 写入数据
		
		fprintf(op, "%lld ", cunru);
		/*fputc()*/
	}
	fclose(op); // 关闭文件
	return 0;
}

long long int jiemiwen = 0;
int shuchu = 0;


void Jiemi()//解密
{
	mpz_init_set_ui(miwen, jiemiwen);
	mpz_pow_ui(miyunsuan, miwen, qmofanshu);
	printf("\n幂运算结果输出:");
	gmp_printf("%Zd\n", miyunsuan);//幂运算输出测试
	mpz_mod(miyunsuan, miyunsuan, qzhishuchengji);
	printf("解密后明文:");
	gmp_printf("%Zd", miyunsuan);
	shuchu = mpz_get_si(miyunsuan);
}


void jmopenbook()
{
	FILE* dp;  FILE* mp;
	int ch1=0;
	long long int shuju = 0;
	int tiaochu = 0;
	dp = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\明文加密.txt", "r");
	mp = fopen("C:\\Users\\Lenovo\\Desktop\\信息安全\\RSA\\Project1\\解密.txt", "w");
	if (dp == NULL) 
	{
		printf("Error opening file\n");
		exit(0);
	}
	while (1)
	{
		shuju = 0;
		while ((ch1 = fgetc(dp)) != ' ')
		{
			//printf("%x", ch1);
			if (ch1 >= '0' && ch1 <= '9')
			{
				shuju = shuju * 10 + (ch1 - '0');
			}
			else
			{
				tiaochu = 1;
				break;
			}
		}
		/*printf("%d", shuju);*/
		if (tiaochu == 1)
		{
			break;
		}
		jiemiwen = shuju;
		printf("\n密文输出:%lld", jiemiwen);
		Jiemi();
		fprintf(mp, "%c", (char)shuchu);
	}
	fclose(mp);
}

int gcd(int A, int B)
{ 
	if (B == 0)
	{
		return A;
	}
	else 
	{
        gcd(B, A % B);
	}
}//欧几里得辗转相除法求最大公约数


void RSA()//私钥、公钥产生
{
    //long long int miyunsuan = 0;
	long long int zhishuchengji = 0;
	long long int Oulashu = 0, Oulazhishu = 0, Oulaxishu = 0;
	long long int mofanshu = 0;
	int tiaochuxunhuan = 0;
	int choice = 0;
	zhishuchengji = zhishuA * zhishuB;
	Oulashu = (zhishuA - 1) * (zhishuB - 1);
	printf("\n欧拉函数为:%lld\n", Oulashu);
	printf("请输入与欧拉函数互质的整数:");
	scanf("%lld", &Oulazhishu);
	if (gcd(Oulashu, Oulazhishu)!=1)
	{
		printf("输入数与欧拉函数不互质");
		exit(0);
	}
	while (1)
	{
		for ( mofanshu = 0; mofanshu <= Oulashu; mofanshu++)
		{
			if(mofanshu*Oulazhishu==Oulaxishu*Oulashu+1)
			{
				tiaochuxunhuan = 1;
				break;
			}
		}
		if (tiaochuxunhuan==1)
		{
			break;
		}
		Oulaxishu += 1;
	}
	printf("\n模反数:%lld", mofanshu);
	printf("\n欧拉系数:%lld\n", Oulaxishu);
	//printf("%d", mofanshu);
	printf("公钥为(%lld %lld)", Oulazhishu, zhishuchengji);
	printf("私钥为(%lld %lld)", mofanshu, zhishuchengji);
	


	while (1)
	{
		printf("\n请选择加密或解密:");
		scanf("%d", &choice);
		if (choice == 1)
		{
			qOulazhishu = Oulazhishu;
			qmofanshu = mofanshu;
			mpz_init_set_ui(qzhishuchengji, zhishuchengji);
			mpz_init(miyunsuan);
			JmOpenbook();
		}
		else if (choice == 2)
		{
			qOulazhishu = Oulazhishu;
			qmofanshu = mofanshu;
			mpz_init_set_ui(qzhishuchengji, zhishuchengji);
			mpz_init(miyunsuan);
			jmopenbook();
		}
		else if (choice == 3)
		{
			break;
		}
	}
}

int Panduan(int n) 
{
	if (n < 2) {
		return 0;
	}
	for (int i = 2; i * i <= n; i++) 
	{
		if (n % i == 0)
		{
			return 0;
		}
	}
	return 1;
}//返回值为1表示是素数,为0则不是

int a = 0, mjg = 0;
mpz_t pd, mijg, mojg, shua, shub;
int feima(int sushu)
{
	srand(time(NULL));

	if (sushu==2)
	{
		return 1;
	}
	if(sushu==1||sushu==0)
	{
		return 0;
	}
	a = rand() % sushu;
	while (a == 0 || a == 1)
	{
		a = rand() % sushu;
	}

	mpz_init_set_ui(shua, sushu);
	mpz_init_set_ui(pd, a);
	mpz_pow_ui(mijg, pd, sushu-1);
	mpz_mod(mojg, mijg, shua);
	mjg = mpz_get_si(mojg);
	//printf("%d", mjg);
	return mjg;
}//费马小定理一次检测

mpz_t suijishu, suijimi, msu, qmo;
int Miller(int msushu)
{
	srand(time(NULL));
	int suiji = 0;
	int jiance = 0;
	int erci = 0;
	int mozhi = 0;

	if (msushu==2||msushu==3)
	{
		return 1;
	}

	erci = (msushu-1)/2;
	mpz_init_set_ui(msu, msushu);
	for ( jiance = 0; jiance < 10; jiance++)
	{
		suiji = rand() % msushu;
		while (suiji == 0 || suiji == 1 || suiji == msushu - 1 )
		{
			suiji = rand() % msushu;
		}
		mpz_init_set_ui(suijishu, suiji);
		mpz_pow_ui(suijimi, suijishu, erci);
		mpz_mod(qmo, suijimi, msu);
		mozhi = mpz_get_si(qmo);
		//printf("%d", mozhi);
		if (mozhi!=1&&mozhi!=msushu-1)
		{
			return 0;
		}
	}
	return 1;
}//Miller_Rabin素性检测二次探测

int main()
{
	//--------------------------------------------------------------随机生成+遍历判断素性
	//srand(time(NULL));
	//while (zhishuA < 100 || zhishuA > 999 || !Panduan(zhishuA)) {
	//	zhishuA = rand() % 900 + 100;
	//}
	//while (zhishuB < 100 || zhishuB > 999 || !Panduan(zhishuB)) {
	//	zhishuB = rand() % 900 + 100;
	//}
	//printf("生成的大素数p为:");
	//printf("%d\n", zhishuA);
	//printf("生成的大素数q为:");
	//printf("%d\n", zhishuB);
	//RSA();

	//-----------------------------------------------------------scanf输入程序测试
	//printf("请输入两个素数:");
	//scanf("%d %d", &zhishuA, &zhishuB);//验证
	//RSA();

    //---------------------------------------------------------------Miller_rabin素性检测

	srand(time(NULL));
	int i = 0, n = 0;
	while (1)
	{
		i = rand() % 900 + 100;
		if (feima(i) == 1)
		{
			if (Miller(i) == 1)
			{
				zhishuA = i;
				break;
			}
		}
	}
	printf("生成素数p为:");
	printf("%d\n", zhishuA);

	while (1)
	{
		n = rand() % 900 + 100;
		if (feima(n) == 1)
		{
			if (Miller(n) == 1)
			{
				if (n!=i)
				{
					zhishuB = n;
					break;
				}

			}
		}
	}
	printf("生成素数q为:");
	printf("%d", zhishuB);
	RSA();

}

五、拓展补充

1.GMP库

    GMP(GNU Multiple Precision Arithmetic Library)是一个用于高精度计算的C/C++库。它可以处理任意长度的整数、有理数和浮点数,并提供了丰富的数学函数和算法,包括加减乘除、取模、幂运算、素数测试、大数质因数分解等。 GMP库的主要特点是速度快、精度高、可移植性好。它可以在多种操作系统和编译器下运行,并且支持多种硬件架构的优化。GMP库还提供了多线程支持和安全的内存管理机制,可以有效地处理大规模计算任务。 在使用GMP库时,需要先定义一个GMP整数类型(mpz_t),然后通过函数调用来进行各种计算操作。

2.资源分享

    本文的RSA加解密系统在程序设计中采用了gmp-6.2.0版本的GMP库,如果根据文章分享的程序无法编译成功,可采用下面链接进行跳转,直接下载资源(包含GMP库和程序源码)。注意,资源下载以后,需要根据文件在电脑中路径更改GMP库在程序里的配置路径。对于VS添加外设库的方法,大家可以通过查阅资料的方式进行学习。

https://download.csdn.net/download/qq_63040545/87857280?spm=1001.2014.3001.5501​​​​​​​

  • 18
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值