一、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