RSA题型整理

同步于博客:http://dragonliu.tk/

0x00 前期准备

01 基础知识

02 Ubuntu 安装gmpy2模块

PARI/GP是一个比较强大的数论库,“针对数论中的快速计算(大数分解,代数数论,椭圆曲线…)而设计”。

需要的依赖库 gmp mpfr mpc

gmp 库安装

sudo apt-get install libgmp-dev

mpfr 库安装

sudo apt-get install libmpfr-dev

mpc 库安装

sudo apt-get install libmpc-dev

gmpy2 安装

#python3
sudo pip3 install gmpy2
#python2
sudo pip install gmpy2

0x01 已知n、e、c,求m

01 思路

  1. 利用 http://factordb.com/ 分解n获得p和q;
  2. 计算d;
  3. 解密得明文m。

02 代码

#!/usr/bin/env python
# coding=utf-8
import gmpy2

#分解n得p,q
p = gmpy2.mpz(...) #填写p
q = gmpy2.mpz(...) #填写q
e = gmpy2.mpz(...) #填写e
c = gmpy2.mpz(...) #填写c

#计算d
phi_n = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n) #求解模逆元
print "private key:"
print d

#求明文
print "plaintext:"
M  =  pow(c, d, p*q)
print '[10进制]: ' + str(M)
flag = str( hex(M) )[2:] #[2:-1]
print '[16进制]: ' + flag
print '[ASCII码]: ' + flag.decode('hex')

0x02 低加密指数分解攻击(e = 1)

01 思路

  1. 加密过程: c ≡ E ( m ) ≡ m e (   m o d   n ) ≡ m (   m o d   n ) c \equiv E(m) \equiv m^{e}(\bmod n) \equiv m(\bmod n) cE(m)me(modn)m(modn),所以明文与密文模n同余;
  2. m = c + n ∗ k ( k = 0 , 1 , 2 , 3... ) m = c + n*k (k=0,1,2,3...) m=c+nk(k=0,1,2,3...),暴力破解即可。

02 代码

#!/usr/bin/env python
# coding=utf-8
import libnum

n = ... #填写n
c = ... #填写c

max_num = 6 #遍历上限

for k in range( max_num ):
    m = c + n*k
    print libnum.n2s(m)

0x03 Rabin加密(e=2)

01 思路

理论知识:我跳

02 代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import libnum
import gmpy2

#从文件中读取密文
f = open('flag.enc','r')
c = f.read()
c = libnum.s2n(c)#字符串转数字

#分解n
p = ... #填写p
q = ... #填写p
n = p * q

#求解mp与mq
mp = pow(c, (p+1)/4, p)
mq = pow(c, (q+1)/4, q)

#求解yp与yq
yp = gmpy2.invert(p, q)
yq = gmpy2.invert(q, p)

#获得四个解
r1 = (yp*p*mq + yq*q*mp) % n
r2 = (-r1) % n
r3 = (yp*p*mq - yq*q*mp) % n
r4 = (-r3) % n

print libnum.n2s( r1 )#数字转字符串
print libnum.n2s( r2 )
print libnum.n2s( r3 )
print libnum.n2s( r4 )

p = q p = q p=q时,使用python命令行将16进制转十进制,然后直接求解 c c c模 n 时的平方根:

n = ...;
c = ...;
PowerMod[c, 1/2, n] 

Wolfram 语言在线编辑:我跳


0x04 flag.enc + pubkey.pem

01 思路

  1. 解压得到两个文件【flag.enc】和【pubkey.pem】,其中【flag.enc】从文件名含有flag可以判断是加密后的密文,【pubkey.pem】是公钥文件,通过公钥文件可以得到e和n;
  2. 通过openssl对公钥文件【pubkey.pem】进行分解,使用命令【openssl rsa -pubin -text -modulus -in warmup -in pubkey.pem】,得到 e【Exponent】和 n【Modulus】;
  3. 其他根据类型判断。

02 代码

#!/usr/bin/env python
# coding=utf-8
import gmpy2
import rsa

#分解n得p,q
n = ... #填写n
p = ... #填写p
q = ... #填写q
e = ... #填写e

#计算私钥
phi_n = (p - 1) * (q - 1)
d = int( gmpy2.invert(e, phi_n) ) #求解模逆元
privatekey = rsa.PrivateKey(n , e , d , p , q) #根据已知参数,计算私钥

with open("./flag.enc" , "rb") as f: #填写文件
    print rsa.decrypt(f.read(), privatekey).decode()  #使用私钥对密文进行解密,并打印

0x05 共模攻击

01 思路

用相同的N,不同的e进行加密的,可以使用共模攻击。

02 代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from libnum import n2s, s2n
from gmpy2 import invert, gcdext

n  = ... #填写n
e1 = ... #填写e1
e2 = ... #填写e2

s = gcdext(e1, e2)  
s1 = s[1]
s2 = -s[2]

#读取密文
file1 = open('flag.enc1', 'r')
c1 = file1.read()
c1 = s2n(c1)#字符串转数字

file2 = open('flag.enc2', 'r')
c2 = file2.read()
c2 = s2n(c2)#字符串转数字

c2 = invert(c2, n)   
m = (pow(c1,s1,n) * pow(c2 , s2 , n)) % n
print n2s(m)

0x06 低加密指数分解攻击(e = 3)

01 思路

  1. 公钥中,e=3,N非常大。
  2. 加密过程: c ≡ E ( m ) ≡ m e (   m o d   n ) ≡ m 3 (   m o d   n ) c \equiv E(m) \equiv m^{e}(\bmod n) \equiv m^3(\bmod n) cE(m)me(modn)m3(modn),所以明文与密文的3次方模n同余;
  3. m = c + n ∗ k ( k = 0 , 1 , 2 , 3... ) m = c + n*k (k=0,1,2,3...) m=c+nk(k=0,1,2,3...),然后开三次方,暴力破解即可。

02 代码

#!/usr/bin/env python
# coding=utf-8
from libnum import s2n, n2s
from gmpy2 import iroot

n = ... #填写n
e = 3

#读取密文
file = open('flag.enc', 'r')
c = file.read()
c = s2n(c)#字符串转数字
file.close()

i = 0 
while True:
    res = iroot( (c + i*n), 3 )
    if( res[1] ==  True):
        print res
        break
    print "i = " + str(i)
    i += 1

m = ... #暴力获得的m
print n2s(m)

0x07 私钥修复+最优非对称加密填充(God Like RSA)

01 思路

压缩包里有一个密文,一个部分缺失的私钥,一个公钥,读公钥可知 N 是 4096 位的,分解无望,肯定要从私钥着手。

  1. 【vscode】打开【private.corrupted】,将对应变量填入下列脚本;
  2. 执行脚本后得到私钥,新建文件【private.pem】并将私钥复制进去;
  3. 然后执行【最优非对称加密填充】脚本。

02 代码

私钥修复脚本

#!/usr/bin/python
#-*- coding:utf-8 -*-

import re
import pickle
from itertools import product
from libnum import invmod, gcd


def solve_linear(a, b, mod):
    if a & 1 == 0 or b & 1 == 0:
        return None
    return (b * invmod(a, mod)) & (mod - 1)  # hack for mod = power of 2


def to_n(s):
    s = re.sub(r"[^0-9a-f]", "", s)
    return int(s, 16)


def msk(s):
    cleaned = "".join(map(lambda x: x[-2:], s.split(":")))
    return msk_ranges(cleaned), msk_mask(cleaned), msk_val(cleaned)


def msk_ranges(s):
    return [range(16) if c == " " else [int(c, 16)] for c in s]


def msk_mask(s):
    return int("".join("0" if c == " " else "f" for c in s), 16)


def msk_val(s):
    return int("".join("0" if c == " " else c for c in s), 16)

#根据文件pubilc.pem得到
E = ...
N = to_n("""...""")

#private.corrupted中的prime1
p_ranges, pmask_msk, pmask_val = msk(""" ... """)

#prime2
q_ranges, qmask_msk, qmask_val = msk(""" ... """)

#privateExponent
_, dmask_msk, dmask_val = msk(""" ... """)

#exponent1
_, dpmask_msk, dpmask_val = msk(""" ... """)

#exponent2
_, dqmask_msk, dqmask_val = msk(""" ... """)


def search(K, Kp, Kq, check_level, break_step):
    max_step = 0
    cands = [0]
    for step in range(1, break_step + 1):
        #print " ", step, "( max =", max_step, ")"
        max_step = max(step, max_step)

        mod = 1 << (4 * step)
        mask = mod - 1

        cands_next = []
        for p, new_digit in product(cands, p_ranges[-step]):
            pval = (new_digit << ((step - 1) * 4)) | p

            if check_level >= 1:
                qval = solve_linear(pval, N & mask, mod)
                if qval is None or not check_val(qval, mask, qmask_msk, qmask_val):
                    continue

            if check_level >= 2:
                val = solve_linear(E, 1 + K * (N - pval - qval + 1), mod)
                if val is None or not check_val(val, mask, dmask_msk, dmask_val):
                    continue

            if check_level >= 3:
                val = solve_linear(E, 1 + Kp * (pval - 1), mod)
                if val is None or not check_val(val, mask, dpmask_msk, dpmask_val):
                    continue

            if check_level >= 4:
                val = solve_linear(E, 1 + Kq * (qval - 1), mod)
                if val is None or not check_val(val, mask, dqmask_msk, dqmask_val):
                    continue

                if pval * qval == N:
                    print "Kq =", Kq
                    print "pwned"
                    print "p =", pval
                    print "q =", qval
                    p = pval
                    q = qval
                    d = invmod(E, (p - 1) * (q - 1))
                    coef = invmod(p, q)

                    from Crypto.PublicKey import RSA
                    print RSA.construct(map(long, (N, E, d, p, q, coef))).exportKey()
                    quit()

            cands_next.append(pval)

        if not cands_next:
            return False
        cands = cands_next
    return True

def check_val(val, mask, mask_msk, mask_val):
    test_mask = mask_msk & mask
    test_val = mask_val & mask
    return val & test_mask == test_val


for K in range(1, E):
    if K % 100 == 0:
        print "checking", K
    if search(K, 0, 0, check_level=2, break_step=20):
        print "K =", K
        break

for Kp in range(1, E):
    if Kp % 1000 == 0:
        print "checking", Kp
    if search(K, Kp, 0, check_level=3, break_step=30):
        print "Kp =", Kp
        break

for Kq in range(1, E):
    if Kq % 100 == 0:
        print "checking", Kq
    if search(K, Kp, Kq, check_level=4, break_step=9999):
        print "Kq =", Kq
        break

解题脚本(最优非对称加密填充)

#!/usr/bin/python
# coding=utf-8
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

with open('pubkey.pem', 'r') as f:
    key = RSA.importKey(f)
    N = key.n
    e = key.e

print N
print e

with open('private.pem', 'r') as f:
    private = RSA.importKey(f)
    oaep = PKCS1_OAEP.new(private)

with open('flag.enc', 'r') as f:
    print oaep.decrypt(f.read())

03 参考

RSA 私钥恢复和最优非对称加密填充


0x08 wiener attack(e特别大)

01 思路

  1. 给出的n分解无望,而且e特别大,利用wiener attack脚本分解;
  2. 然后利用一般方法求解即可。

02 代码

import gmpy2
import libnum

def continued_fractions_expansion(numerator,denominator):#(e,N)
    result=[]
 
    divident=numerator%denominator
    quotient=numerator/denominator
    result.append(quotient)
 
    while divident!=0:
        numerator=numerator-quotient*denominator
 
        tmp=denominator
        denominator=numerator
        numerator=tmp
 
        divident=numerator%denominator
        quotient=numerator/denominator
        result.append(quotient)
 
    return result
 
def convergents(expansion):
    convergents=[(expansion[0],1)]
    for i in range(1,len(expansion)):
        numerator=1
        denominator=expansion[i]
        for j in range(i-1,-1,-1):
            numerator+=expansion[j]*denominator
            if j==0:
                break
            tmp=denominator
            denominator=numerator
            numerator=tmp
        convergents.append((numerator,denominator))#(k,d)
    return convergents
 
def newtonSqrt(n):
    approx = n/2
    better = (approx + n/approx)/2
    while better != approx:
        approx = better
        better = (approx + n/approx)/2
    return approx
 
def wiener_attack(cons,e,N):
    for cs in cons:
        k,d=cs
        if k==0:
            continue
        phi_N=(e*d-1)/k
        #x**2-((N-phi_N)+1)*x+N=0
        a=1
        b=-((N-phi_N)+1)
        c=N
        delta = b*b - 4*a*c
        if delta<=0:
            continue
        x1= (newtonSqrt(delta)-b)/(2*a)
        x2=-(newtonSqrt(delta)+b)/(2*a)
        if x1*x2==N:
            return [x1,x2,k,d]
 
 
N = ...
e = ...
 
expansion = continued_fractions_expansion(e,N)
cons = convergents(expansion)
 
p, q, k, d = wiener_attack(cons, e, N)
print p
print q

c = ... #密文

d = gmpy2.invert(e, (p - 1) * (q - 1))
m = pow(c, d, N)
print libnum.n2s(m)

0x0 reference

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值