2019广东外语外贸大学CTF新手赛-密码学-RSA题解
题面
n=100000463700003241
e=17
密文:
1 30876669824664856 2 30876669824664856 3 30876669824664856 4 77223069454039395 5 82242047293765567 6 24233423779340487 7 15484404506016653 8 98468829472585108 9 75555576962313807 10 42582342140719252 11 99781733958238709 12 77223069454039395 13 82242047293765567 14 75555576962313807 15 69867427593611884 16 52722366704774877 17 33922570905674122 18 87013233200361725 19 86151045766218388 20 87013233200361725 21 64866053818555083 22 79408929452159624 23 55986180764196875 24 40591873855071567 25 24233423779340487 26 95973722241816075 27 52722366704774877 28 91914702290680719 29 64866053818555083 30 75555576962313807 31 94279162165241579 32 87013233200361725 33 95973722241816075 34 52722366704774877 35 91914702290680719 36 52722366704774877 37 88815074647411982 38 88815074647411982 39 59675813170817903 40 7715722791264684 41 59675813170817903 42 7715722791264684 43 22373291521975461 44 59675813170817903 45 77223069454039395 46 10528298511885015 47 94279162165241579 48 69867427593611884 49 98468829472585108 50 51 52 11080055013504306 53 55617650536440882 54 52950329497873673 55 69867427593611884 56 55617650536440882 57 43009662331064017 58 32609881881220234 59 38938329728907166 60 10528298511885015 61 87013233200361725 62 24233423779340487 63 32520592532905261 64 40591873855071567 65 75555576962313807
分析:
题面已明示是RSA加密,已公开n与公钥e,n为1e18内的数字(64位).要爆破RSA,显然是先分析n的值。
n的值是由两个素数p和q相乘得出,所以我们需要将n因数分解。
n为64位,可能有1e18内的任意一对素数生成,计算机每秒运行少于1e9次,所以n^2暴力的素数表爆破是时间过长的。我们需要优化爆破算法。
我们可以取一个素数p,q=n/p,检测q是否为素数,q*p是否为n,以此来爆破p q(耗时1200秒左右)
根据这个思路,我们用米勒素数判定来检测p q即可实现(素数打表速度过慢)
也可以通过生成1e9的素数表,通过二分搜索来实现爆破,速度更快
代码如下:
1 #include <iostream> 2 #include <map> 3 #include <time.h> 4 using namespace std; 5 #define ll long long 6 /** 7 Miller_Rabin 算法进行素数测试 8 快速判断一个<2^63的数是不是素数,主要是根据费马小定理 9 */ 10 //#define ll __int128 11 const int S=8; ///随机化算法判定次数 12 ll MOD; 13 ///计算ret=(a*b)%c a,b,c<2^63 14 ll mult_mod(ll a,ll b,ll c) 15 { 16 a%=c; 17 b%=c; 18 ll ret=0; 19 ll temp=a; 20 while(b) 21 { 22 if(b&1) 23 { 24 ret+=temp; 25 if(ret>c) 26 ret-=c;//直接取模慢很多 27 } 28 temp<<=1; 29 if(temp>c) 30 temp-=c; 31 b>>=1; 32 } 33 return ret; 34 } 35 36 ///计算ret=(a^n)%mod 37 ll pow_mod(ll a,ll n,ll mod) 38 { 39 ll ret=1; 40 ll temp=a%mod; 41 while(n) 42 { 43 if(n&1) 44 ret=mult_mod(ret,temp,mod); 45 temp=mult_mod(temp,temp,mod); 46 n>>=1; 47 } 48 return ret; 49 } 50 51 ///通过费马小定理 a^(n-1)=1(mod n)来判断n是否为素数 52 ///中间使用了二次判断,令n-1=x*2^t 53 ///是合数返回true,不一定是合数返回false 54 bool check(ll a,ll n,ll x,ll t) 55 { 56 ll ret=pow_mod(a,x,n); 57 ll last=ret;//记录上一次的x 58 for(int i=1;i<=t;i++) 59 { 60 ret=mult_mod(ret,ret,n); 61 if(ret==1&&last!=1&&last!=n-1) 62 return true;//二次判断为是合数 63 last=ret; 64 } 65 if(ret!=1) 66 return true;//是合数,费马小定理 67 return false; 68 } 69 70 71 ///Miller_Rabbin算法 72 ///是素数返回true(可能是伪素数),否则返回false 73 bool Miller_Rabbin(ll n) 74 { 75 if(n<2) return false; 76 if(n==2) return true; 77 if((n&1)==0) return false;//偶数 78 ll x=n-1; 79 ll t=0; 80 while((x&1)==0) 81 { 82 x>>=1; 83 t++; 84 } 85 srand(time(NULL)); 86 for(int i=0;i<S;i++) 87 { 88 ll a=rand()%(n-1)+1; // 生成随机数 0<a<=n-1 去试试 89 if(check(a,n,x,t)) 90 return false; 91 } 92 return true; 93 } 94 int main(){ 95 ll n,p,q; 96 cin>>n; 97 for(int i=0;i<2000000000;i++){ 98 if(Miller_Rabbin(i)){ 99 p=n/i; 100 if(Miller_Rabbin(p)&&i*p==n){ 101 cout<<p<<" "<<i<<'\n'; 102 break; 103 } 104 } 105 } 106 }
爆破出p q 后我们手搓RSA加密来生成密钥
(拓展欧几里得算法、欧拉函数、快速幂运算)
代码如下:
1 #include <iostream> 2 using namespace std; 3 #define ll long long 4 ll exgcd(ll a,ll b,ll &x,ll &y){ 5 6 if(a==0&&b==0) return -1; 7 if(b==0){ 8 x=1,y=0; 9 return a; 10 } 11 ll d=exgcd(b,a%b,y,x); 12 //cout<<a<<" "<<b<<'\n'; 13 y-=a/b*x; 14 return d; 15 } 16 ll poww(ll a,ll b,ll c){ //快速幂取模 17 ll ans(1),base=a%c; 18 //a=a%c; 19 while(b){ 20 if(b&1) ans=(ans*base)%c; 21 base=base*base%c; 22 b>>=1; 23 } 24 return ans; 25 } 26 27 int main(){ 28 int flag(0); 29 ll p,q,ou,ed,n,e(17),x(1),y(1),num1,ans,s; 30 cin>>flag; 31 if(flag==1){ 32 cout<<"依次输入 p q 与 e:"; 33 cin>>p>>q; 34 n=p*q; 35 ou=(p-1)*(q-1); 36 cin>>e; 37 num1=exgcd(e,ou,x,y); 38 //y=y+e/ou*x; 39 if(x<=0)x=(x+ou)%ou; 40 //x=(x+e)%e; 41 cout<<"N值:"<<n<<'\n'; 42 cout<<"公钥:"<<e<<'\n'; 43 cout<<"密钥:"<<x<<'\n'; 44 return 0; 45 } 46 if(flag==2){ 47 cin>>s>>e>>n; 48 ans=poww(s,e,n); 49 cout<<ans<<'\n'; 50 } 51 }
得到密钥后我们手写py脚本,通过密文算出明文(快速幂运算)
1 def poww(a,n,mod): 2 ret=1 3 tmp=a%mod 4 while(n>0): 5 if(n%2!=0): 6 ret=(ret*tmp)%mod 7 tmp=tmp*tmp%mod 8 n>>=1 9 return ret 10 s1=e=n=1.0 11 n=int(input()) 12 e=int(input()) 13 while(True): 14 s1=int(input()) 15 ans=poww(s1,e,n) 16 print(ans)
观察生成的明文,我们发现其都在ASCII码的范围之内,尝试转出字符,可得到网址与博客密码
进入博客后可以看到一篇加密后的林肯的《底斯堡演讲》
因为题面提示,此密文为古典密码,我们可以通过分析字频(英文字母e i s)或者暴力破解(跑所有古典密码解密方案,并用词典进行对比),可以发现此加密为凯撒加密,即可得到flag
坑点、考点、思路总结:
首先你得会RSA加密(我学了一小时不到就来出题了)
在不会各种算法、数论知识的情况下可以借助外界工具解题,如在线因数分解,py库提供的种种数学调用
本题出题的目的是考察算法知识、数论知识、与对数据的分析判断,很多同学使用了各种外界工具来辅助解题,但依旧希望各位能够去了解、学习上述知识点与算法,为后续的密码学学习打下基础
posted on 2019-11-23 01:29 Where_Free 阅读(754) 评论(1) 编辑 收藏 举报