前言:最近好久没更新博客,因为在忙着考雅思还有一直也在看CTF(=_=上个星期玩了一下腾讯的CTF。。。。这酸爽,我一直都认为比赛是最容易提升自己实力的一种途径,上次比赛也发现了自己的不足,继续努力吧少年)今天看了一天的RSA算法。以前在上课的时候,记得老师光讲这个算法就讲了有两堂课。现在在看一下其实也不是很难(别骗自己了=_=)所以写这篇文章记录一下,顺便加深一下自己的印象
0x1
首先开始之前我们要补充一下关于RSA的一些常识
(1) 选择一对不同的、足够大的素数p,q。
(2)计算n=pq。
(3)计算f(n)=(p-1)(q-1),同时对p, q严加保密,不让任何人知道。
(4)找一个与f(n)互质的数e,且1<e<f(n)。
(5)计算d,使得de≡1 mod f(n)。这个公式也可以表达为(d*e-1)% f(n)=0
这里要解释一下,≡是数论中表示同余的符号。公式中,≡符号的左边必须和符号右边同余,也就是两边模运算结果相同。
显而易见,不管f(n)取什么值,符号 右边1 mod f(n)的结果都等于1;符号的左边d与e的乘积做模运算后的结果也必须等于1。
这就需要计算出d的值,让这个同余等式能够成立。
(6)公钥KU=(e,n),私钥KR=(d,n)。
(7)加密时,先将明文变换成0至n-1的一个整数M。若明文较长,可先分割成适当的组,然后再进行交换。设密文为C,
则加密过程为:C≡M^e(mod n)。
(8)解密过程为:M≡C^d(mod n)。
同余:数论中的重要概念。给定一个正整数m,如果两个整数a和b满足(a-b)能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,
记作a≡b(mod m)。对模m同余是整数的一个等价关系
有了上面的RSA基础可以知道RSA中的
p: 第一个大素数
q: 第二个大素数
模数n: n = p*q
f(n): (p-1)*(q-1)
公钥指数e: 与 f(n)互质, 且 1 < e < f(n)
私钥指数d: 满足e * d ≡ 1 (mod f(n))
公钥 = {n, e},一般公开
私钥 = {d, e}
0x2
就拿实验吧上面的一个题做这个例子吧因为最近也一直在玩CTF
http://ctf5.shiyanbar.com/crypto/RSAROLL.txt
第一行我们看到了这样一组数,直觉告诉我们这个很有可能是公钥(当然也不排除出题师傅直接把私钥给你,这样出题师傅是不是就太可爱了呀)
{920139713,19}
根据我们上面补充的知识知道要按照步骤来获取私钥
(1) n = 920139713 e =19
(2) 这时候我们要将整数n分解为两个素数,我们可以使用在线网站直接来获取
http://www.factordb.com/index.php
当然我们可以写代码来实现
如下:def divde(n):
start =2
while start < math.sqrt(n):
if (n%start==0):
record = start
print(“大整数可以分解为:”,str(start),str(int(n/base)))
break
base+=1
return (start,int(n/base))
(3) 经过上面的操作我们可以获得
p = 18443,q = 49891,f(n) = (p-1)*(q-1)/p,q 是上n分解的两个质数,f(n)是欧拉函数
(4) 根据我们现在已经获得的条件以及前面补充的RSA知识我们知道只要找到一个满足e * d ≡ 1 (mod f(n)) 这个方程式的d,我们就成功获取的私钥(于是我就自己写了一个函数跑了一下,跑了好长时间仍然没有结果,在我快要放弃的时候我忽然想到了gmpy2,其中有一个神奇的函数)
(g,d,_) = gcdext(e,f(n)) //不知道为什么是这种格式但是它的效果是与e * d ≡ 1 (mod f(n)) 使用此函数只用了不到一秒的时间就获得了私钥d= 96849619
(5) 至此成功拿到了私钥,剩下的就剩解密密文
利用pow(i,d,n))解密密文 //这里的i就是密文,d是我们刚才获取的私钥,n公钥如下图一
图一获取的是ASCII
(6) 最后将ASCII转化为相应的字符(这里我用了自己写的一个小脚本工具)
0x3代码部分
#-*- coding:utf-8 -*-
from gmpy2 import *
from Crypto.Util.number import *
利用gmpy2中的gcdext()函数 实现的功能是 (d*e-1)% f(n)=0从而获取私钥d
def decrypt():
flag = ''
n = 920139713
e =19
p = 18443
q = 49891
phi_n = (p-1)*(q-1)
(g,d,_) = gcdext(e,phi_n) #利用此函数可以获取私钥d值
print("Privcate key:",str(g),str(d))
plaintext =""" 704796792
752211152
274704164
18414022
368270835
483295235
263072905
459788476
483295235
459788476
663551792
475206804
459788476
428313374
475206804
459788476
425392137
704796792
458265677
341524652
483295235
534149509
425392137
428313374
425392137
341524652
458265677
263072905
483295235
828509797
341524652
425392137
475206804
428313374
483295235
475206804
459788476
306220148
"""
plaintext = plaintext.split()
for i in plaintext:
#利用pow(l,d,n) l的d次方然后取n的余数
i = int(i)
print((pow(i,d,n)),end=' ')
if __name__=='__main__':
decrypt()
0x4 后记
嘻嘻,前段时间女朋友闹着分手,上个星期她又打电话个更说对不起,我们重新和好吧。嗯~ o(* ̄▽ ̄*)o知错就改还是好同志嘛!之前好伤心呢,现在一起都过去了。我会以更加昂扬的斗志生活。后后记+_+ pwn好难呀,不会呀!要多多看看这方面的文章。