題面
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庫提供的種種數學調用
本題出題的目的是考察算法知識、數論知識、與對數據的分析判斷,很多同學使用了各種外界工具來輔助解題,但依舊希望各位能夠去了解、學習上述知識點與算法,為后續的密碼學學習打下基礎