RSA(第十届蓝桥杯省赛C++A组E题) 解题报告 Apare_xzc

RSA(第十届蓝桥杯省赛C++A组E题) 解题报告

xzc 2019/4/4 (边看World Final B站直播边写的)


题目链接


题面:(开始码字)

  RSA是一种经典的加密算法。它的基本加密过程如下。

  首先生成两个大质数p,q, 令n = p*q,设d与(p-1)*(q-1)互质,则可以找到e,使得d*e除以(p-1)*(q-1)的余数为1

  n,d,e组成了私钥,n,d构成了公钥。

  当使用公钥加密一个整数X时(X<=n-1),计算C = X^d mod n,则C是加密后的密文。

  当收到密文C时,可以使用私钥解开,计算公式为:X = C^e mod n。

  例如:当p = 5, q = 11, n = 55, e = 27。

  若加密数字24,得24^3 % 55 = 19。

  解密数字19,得19^27 % 55 = 24。

  现在你知道公钥中n = 1001733993063167141,d = 212353,同时,你截获了别人发送的密文C = 20190324,请问,原文是多少?

码字码得好累啊~


题意:
  给一个n,分解质因数成p*q, 然后计算d关于(p-1)*(q-1)的逆元,结果为e,
求C^e % n的结果
  n = 1001733993063167141
  d = 212353
  c = 20190324


解题过程:

  1. 我们肯定要把n分解质因数,我首先想到了欧拉筛素数,然后晒了一发1E8以内的素数,想用这些素数去分解n
//筛素数
const int maxn = 1e8+10;
int a[maxn];
int sushu[maxn/10];
bool notPrime[maxn]; ///筛素数分解不出来,目测是9位数*10位数 
int cnt;
void getPrime(int n)
{
	for(int i=2;i<=n;++i)
	{
		if(!notPrime[i]) sushu[cnt++] = i;
		for(int j=0;j<cnt&&1ll*i*sushu[j]<=n;++j)
		{
			notPrime[i*sushu[j]] = true;
			if(i%sushu[j]==0) break; 
		} 
	}
}

然后分解质因数:

//#define LL long long
void fenjie(LL x)
{
	cout<<x<<" = 1";
	for(int i=0;i<cnt&&sushu[i]<=x/sushu[i];++i)
	{
		while(x%sushu[i]==0)
		{
			cout<<" * "<<sushu[i];
			x /= sushu[i];
		}
	}
	if(x>1) cout<<" * "<<x;
	cout<<endl;
}

结果是这样的:1001733993063167141 = 1 * 1001733993063167141
显然没分解出来,就是说p和q都是大于1E8的大质数
欧拉筛筛不出来,我又不会杜教筛,所以还是暴力吧

void BF(LL x) //蛮力法
{
	cout<<x<<" = "; 
	for(LL i=1e8+1;i<x;i+=2)
	{
		if(x%i==0)
			cout<<i<<" * ",x/=i;
	}
	cout<<x<<endl;
}

结果:

1001733993063167141 = 891234941 * 1123984201

好了,现在我们知道了:
p = 891234941
q = 1123984201
这种题,暴力也用不了10s,以后还是写暴力吧…

LL y = (p-1)*(q-1); //y = 1001733991047948000

接下来我们来求逆元:
因为这个mod = y = 1001733991047948000,它不是个质数
我们要求:
d(212353)关于y(1001733991047948000)的乘法逆元

  • 法一:求y的欧拉函数k = euler(y), rev = d^(k-1)%mod
  • 法二:用拓展欧几里得算法:exgcd复习<-戳这里

我用的exgcd:

//#define LL long long
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
	if(b==0)
	{
		d = a; x = 1; y = 0; return;
	}
	exgcd(b,a%b,d,y,x);
	y -= (a/b)*x;
}
LL rev(LL t,LL m)
{
	LL d,x,y;
	exgcd(t,m,d,x,y);
	return (x%m+m)%m;
}
LL e = rev(d,y); //e = 823816093931522017

然后,我们求c^e%mod
c = 20190324
e = 823816093931522017
mod = 1001733993063167141
注意:
由于这里的mod>1e18太大,快速幂相乘的时候会爆long long的!
这个时候,我们就要用到快速乘

LL fast_product(LL a,LL b,LL mod)//快速乘
{
	LL ans = 0;
	while(b)
	{
		if(b&1) ans = (ans+a)%mod;
		a = (a+a)%mod;
		b>>=1;
	}
	return ans;
} 

然后,我们用快速乘来写快速幂

LL fast_pow(LL a,LL b,LL mod)//快速幂
{
	LL ans = 1;
	while(b)
	{
		if(b&1) ans = fast_product(ans,a,mod);
		a = fast_product(a,a,mod);
		b>>=1;		
	} 
	return ans;
}

最后答案为:579706994112328949


附上完整代码:

///Author: xzc
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e8+10;
const LL n = 1001733993063167141ll;
const LL p = 891234941ll;
const LL q = 1123984201ll;
const LL d = 212353;
const LL c = 20190324;
int a[maxn];
int sushu[maxn/10];
bool notPrime[maxn]; ///筛素数分解不出来,目测是9位数*10位数 
int cnt;
void getPrime(int n)
{
	for(int i=2;i<=n;++i)
	{
		if(!notPrime[i]) sushu[cnt++] = i;
		for(int j=0;j<cnt&&1ll*i*sushu[j]<=n;++j)
		{
			notPrime[i*sushu[j]] = true;
			if(i%sushu[j]==0) break; 
		} 
	}
	for(int i=0;i<20;++i) cout<<sushu[i]<<" ";cout<<endl;
}
void fenjie(LL x)
{
	cout<<x<<" = 1";
	for(int i=0;i<cnt&&sushu[i]<=x/sushu[i];++i)
	{
		while(x%sushu[i]==0)
		{
			cout<<" * "<<sushu[i];
			x /= sushu[i];
		}
	}
	if(x>1) cout<<" * "<<x;
	cout<<endl;
}
void BF(LL x)
{
	cout<<x<<" = "; 
	for(LL i=1e8+1;i<x;i+=2)
	{
		if(x%i==0)
			cout<<i<<" * ",x/=i;
	}
	cout<<x<<endl;
}
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
	if(b==0)
	{
		d = a; x = 1; y = 0; return;
	}
	exgcd(b,a%b,d,y,x);
	y -= (a/b)*x;
}
LL rev(LL t,LL m)
{
	LL d,x,y;
	exgcd(t,m,d,x,y);
	return (x%m+m)%m;
}
LL fast_product(LL a,LL b,LL mod)
{
	LL ans = 0;
	while(b)
	{
		if(b&1) ans = (ans+a)%mod;
		a = (a+a)%mod;
		b>>=1;
	}
	return ans;
} 
LL fast_pow(LL a,LL b,LL mod)
{
	LL ans = 1;
	while(b)
	{
		if(b&1) ans = fast_product(ans,a,mod);
		a = fast_product(a,a,mod);
		b>>=1;		
	} 
	return ans;
}
int main()
{
	//getPrime(maxn-5);
	//fenjie(n);
	BF(n);
	LL y = (p-1)*(q-1);
	LL e = rev(d,y);
	LL answer = fast_pow(c,e,n);
	cout<<answer<<endl;

	return 0;
}

后记: 大数分解质因数还是很难的,18位的大数暴力分解了出来,位数再多一点儿,没有私钥,是很难解码的~


全部评论

相关推荐

看网上风评也太差了
投递万得信息等公司8个岗位 >
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务