zoukankan      html  css  js  c++  java
  • CTF中的RSA 算法

    1.质数(素数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
    2.合数是指比1大但不是素数的数
    3.约数(因数)整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为 b的倍数,b称为a的约数
    4.互质数:如果两个整数a,b的最大公因数(greatest common divisor)为1,即gcb(a,b)=1,那么称a,b两数互质
    5.欧拉函数是指设m为正整数,则1,2,3,4.......,m中与m互素的整数的个数记为φ(m),叫做欧拉函


    RSA加解密涉及变量

    N(n):模数(modulus)
    
    p 和 q :N的两个因子(factor)
    
    e 和 d:(密钥) 互为模反数的两个指数(exponent)
    
    c 和 m:分别是密文和明文,这里一般指的是一个十进制的数还有一个就是n的欧拉函数值
    
    欧拉函数值:r
    
    pow(x, y, z):效果等效pow(x, y)1 % z, 先计算x的y次方,如果存在另一个参数z,需要再对结果进行取模。
    

    RSA 密钥流程

    1.选择两个大的参数,计算出模数 N = p * q
    
    2.计算欧拉函数 φ = (p-1) * (q-1),然后选择一个e(1<e<φ),并且e和φ互质(互质:公约数只有1的两个整数)
    
    3.选一个整数e,满足条件1<e<φ(m),且gcd(φ(m),e)=1。
    
    4.取e的模反数d,计算方法为:e * d ≡ 1 (mod φ) (模反元素:如果两个正整数e和n互质,那么一定可以找到整数d,使得 e * d - 1 被n整除,或者说e * d被n除的余数是1。这时,d就叫做e的“模反元素”。欧拉定理可以用来证明模反元素必然存在。两个整数a,b,它们除以整数M所得的余数相等:a ≡ b(mod m),比如说5除3余数为2,11除3余数也为2,于是可写成11 ≡ 5(mod 3)。)
    
    5.对明文m进行加密:c = pow(m, e, N),可以得到密文c。
    
    6.对密文c进行解密:m = pow(c, d, N),可以得到明文m。
    
    7.以{e,n}为公开密钥,{d,n}为秘密密钥。
    
    

    对于RSA加密算法,公钥{e,n}为公钥,可以任意公开,破解RSA最直接(亦或是暴力)的方法就是分解整数N,然后计算欧拉函数φ(n)=(p-1) * (q-1),再通过d * e ≡ 1 mod φ(N),即可计算出 d,然后就可以使用私钥{d,n}通过m = pow(c,d,N)解密明文。

    常见攻击方法

    已知pqe或者已知ne求出d

    import gmpy2
    p = gmpy2.mpz(18443)#初始化大整数
    q = gmpy2.mpz(49891)
    e = gmpy2.mpz(19)
    phi_n = (p-1)*(q-1)
    d = gmpy2.invert(e,phi_n)  # invert(e,r)返回d使得e * d == 1 mod r,如果不存在d,则返回0
    print("p={0},q={1},e={2}".format(p,q,e))
    print("d is:
    %s"%d)
    
    

    注:gmpy2:开源的高精度算数运算库https://blog.csdn.net/x_yhy/article/details/83903367

    ​ 分解得到p q可以通过在线网站http://www.factordb.com/index.php

    已经求出dnc,然后可以求出相应的明文m

    #求明文
    import gmpy2
    n = 920139713   #模数
    d = 96849619    #密钥
    c = """
    704796792
    752211152
    274704164
    ...  #密文
    """
    result = ""
    c_list = c.split()
    #print(c_list)
    for i in c_list:
        result += chr(pow(int(i),d,n))
    print(result)
    
    

    已知cen求m

    结合以上两种方法,在知道n的前提下可求·pq,利用pqe可以求出d,,从而因为已知dnc,求出相应的明文m


    利用n的公约数

    当题目给出若干个模数n1,n2......,且模数很大。如果两次加密的n1n2具有相同的素因子,那么我们可以利用欧几里德算法直接分解n1n2.从而计算出两个n的最大公约数p

    素因子的定义:对于一个数n来说,将它的因子拆到若干个素数相乘,这些素数被称为n的素因子。
    比如 12可以被拆为2 6
    6不是质数,可以继续拆为2*3
    所以最后12的素因子就是 2, 3(不计重复元素)

    识别此类题目,通常会发现题目给了若干个n,均不相同,并且都是2048bit,4096bit级别,无法直接分解http://www.factordb.com/index.php,并且明文都没什么联系,e也一般取65537。

    #-*-coding:utf-8-*-
    '''
    求两个数的最大公约数
    算法参考:https://zhidao.baidu.com/question/36550887.html
    by:reborn
    '''
    import gmpy2
    n1=
    n2=
    def gys1(n1,n2):    #辗转相除法(欧几里德算法)
        if n1<n2:
            n1,n2=n2,n1
        while n2!=0:
            temp=n1%n2
            n1=n2
            n2=temp
        return n1
    def gys2(n1,n2):    #更相减损法
        while n1!=n2:
            if n1<n2:
                n1,n2=n2,n1
            temp=n1-n2
            n1=temp
        return n1
    p=gys2(n1,n2)
    print ("p=",p)
    
    #求q1,q2
    
    q1=n1//p
    q2=n2//p
    print("q1=",q1)
    print("q2=",q2)
    
    #求d_1,d_2
    
    p0 = gmpy2.mpz(p)#初始化大整数
    q_1 = gmpy2.mpz(q1)
    q_2 = gmpy2.mpz(q2)
    e = gmpy2.mpz(65537)
    r_1 = (p0-1)*(q_1-1)
    r_2 = (p0-1)*(q_2-1)
    d_1 = gmpy2.invert(e,r_1)  # invert(e,r)返回d使得e * d == 1 mod r,如果不存在d,则返回0
    d_2 = gmpy2.invert(e,r_2)
    print("d_1=",d_1)
    print("d_2=",d_2)
    
    # 求c1,c2
    
    c1=
    c2=
    m1 = pow(c1, d_1, n1)
    m2 = pow(c2, d_2, n2)
    print("m1=",m1)
    print("m2=",m2)
    
    

    根据欧几里德算法算出的p之后,再用n除以p即可求出q,由此可以得到的参数有pqne,再使用常规方法计算出d,即可破解密文。

    m = pow(c, d, N),可以得到明文m


    共模攻击

    如果在RSA的使用中使用了相同的模n对相同的明文m进行了加密,那么就可以在不分解n的情况下还原出明文m的值。

    c1 = m^e1 mod n
    c2 = m^e2 mod n

    识别:非常简单,若干次加密,每次n都一样,明文根据题意也一样即可。


    已知私钥文件、c求m

    题目中给出了私钥文件private.pem和flag.enc

    pem文件通常是包含了-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----,是 Base64 编码的二进制内容
    使用私钥解密密文的方式

    使用openssl工具
    利用如下命令:

    rsautl -decrypt -in flag.enc(密文名称) -inkey private.pem


    已知公钥文件、c求m

    题目中给出了public.pem和密文flag.enc

    openssl rsa -pubin -text -modulus -in warmup -in pubkey.pem
    ​ [提取出pubkey.pem中的参数]

    得到n,化为十进制

    将n分解为P,q

    python rsatool.py -o private.pem -e 65537 -p 275127860351348928173285174381581152299 -q 319576316814478949870590164193048041239

    [使用rsatool生成私钥文件: private.pem]

    openssl rsautl -decrypt -in flag.enc -inkey private.pem


    低加密指数攻击

    在RSA中e也称为加密指数。由于e是可以随意选取的,选取小一点的e可以缩短加密时间,但是选取不当的话,就会造成安全问题。

    e=3时的小明文攻击

    当e=3时,如果明文过小,导致明文的三次方仍然小于n,那么通过直接对密文三次开方,即可得到明文。

    (1)m3<n,也就是说m3=c;
    (2)m3>n,即(m3+kn)mod n=c(爆破k,不知道k取什么值)。

    • 第一种情况 根据 c = pow(m, e, N) 可知:

    当e=3时,如果明文过小,导致明文的三次方仍然小于n,那么通过直接对密文三次开方,即可得到明文。

    • 第二种情况 如果明文的三次方比n大,但是不够大,那么设k,有: c=(m^3+kn)mod n

    爆破k,如果(c-kn)能开三次根式,那么可以直接得到明文。

    识别:

    推荐在e=3的时候首先尝试这种方法。

    openssl rsa -pubin -in pubkey.pem (读取公钥内容)
    openssl rsa -pubin in pubkey.pem -text(以文本格式输出公钥内容),从这一步可以知道e的值

    从而判断为低加密指数攻击


    低加密指数广播攻击

    低加密指数广播攻击,即如果选取的加密指数较低,并且使用了相同的加密指数给一个接收者发送了相同的信息(或者给一群接收者发送了相同的信息),那么可以进行广播攻击得到明文。

    假如我们需要将一份明文进行多份加密,但是每份使用不同的密钥,密钥中的模数n不同但指数e相同且很小,我们只要拿到多份密文和对应的n就可以利用中国剩余定理进行解密。

    适用

    只要满足以下情况,我们就可以考虑实用低加密指数广播攻击:

    1.加密指数e非常小
    2.一份明文使用不同的模数n,相同的加密指数e进行多次加密
    3.可以拿到每一份加密后的密文和对应的模数n、加密指数e

    低加密指数广播攻击脚本

    # coding:utf8
    
    from struct import pack,unpack
    import zlib
    import gmpy
    def my_parse_number(number):
        string = "%x" % number
        #if len(string) != 64:
        #    return ""
        erg = []
        while string != '':
            erg = erg + [chr(int(string[:2], 16))]
            string = string[2:]
        return ''.join(erg)
    def extended_gcd(a, b):
        x,y = 0, 1
        lastx, lasty = 1, 0
        while b:
            a, (q, b) = b, divmod(a,b)
            x, lastx = lastx-q*x, x
            y, lasty = lasty-q*y, y
        return (lastx, lasty, a)
    def chinese_remainder_theorem(items):
      N = 1
      for a, n in items:
        N *= n
      result = 0
      for a, n in items:
        m = N/n
        r, s, d = extended_gcd(n, m)
        if d != 1:
          N=N/n
          continue
          #raise "Input not pairwise co-prime"
        result += a*s*m
      return result % N, N
      //中国剩余定理 , 输入多组c和多组n,以及较小的指数e
    sessions=[
    {"c": , "e": , "n": },
    {"c": , "e": , "n": },
    {"c": , "e": , "n": }
    ]
    
    data = []
    for session in sessions:
        e=session['e']
        n=session['n']
        msg=session['c']
        data = data + [(msg, n)]
    print "Please wait, performing CRT"
    x, n = chinese_remainder_theorem(data)
    e=session['e']
    realnum = gmpy.mpz(x).root(e)[0].digits()
    print my_parse_number(int(realnum))
    

    n可以分解为多个素数

    使用公钥加密和使用私钥解密流程(中国剩余定理):
    准备
    首先,我们需要在在生成私钥公钥时,多生成几个数:
    我们的d是e对phi(n)的逆元,我们现在需要另外2个逆元(分别是对(p-1)和(q-1)的),既
    1:计算dp,使得dp * e = 1 mod(p-1)
    2:计算dq,使得dq * e = 1 mod(q-1)
    此外需要第三个元素,既q对p的逆元
    3:计算qInv,使得qInv * q = 1 mod p
    ​ 1 2 3 都作为私钥的一部分。

    dp = d mod p-1
    dq = d mod q-1

    计算:

    使用公钥加密:
    若要加密明文m,则需要计算c = m^e mod n,c为密文。

    使用私钥解密:
    1:m1=c^dp mod p
    2:m2=c^dq mod q
    3:h= (qInv((m1 - m2)mod p)) mod p
    4:m = m2 + h
    q
    m就是明文。

    例:n=17947
    ​ e=3
    ​ c=8363
    ​ m=???

    import gmpy2
    n=17947
    p=137
    q=131
    e=3
    c=8363
    dp=gmpy2.invert(e,p-1)
    dq=gmpy2.invert(e,q-1)
    m1=pow(c,dp,p)
    m2=pow(c,dq,q)
    qInv=gmpy2.invert(q,p)
    h=(qInv*((m1-m2)% p)) % p
    m=m2+h*q
    print(m)
    

    多素数

    例:n=p1p2p3=2279269
    ​ p1=137
    ​ p2=131
    ​ p3=127
    ​ e=19

    预先计算:
    dp = 19^-1 mod 137-1 = 43
    dq = 19^-1 mod 131-1 = 89
    dr = 19^-1 mod 127-1 = 73
    
    若要解密密文 768924,则先计算
    1:m1=768924^43 mod 137 = 102
    2:m2=768924^89 mod 131 = 120
    3:m3=768924^73 mod 127 = 5
    
    等式1与等式2连列方程组计算:
    qInv = 114
    h = (qInv*((m1 - m2)mod p)) mod p = 3
    m12 = m2 + h*q = 120 + 3*131 = 513
    
    所以等式1与等式2的通用解为:513+k1*(131*137)
    所以结合等式3问题可以变为:
    m1=513  p=17947
    m2=5    q=127
    qInv*q≡ 1 mod p    ——>qInv=10316
    h = (10316*((513 - 5)mod 17947)) mod 17947 =4
    m = 5 + 4*127 = 513
    ......
    
    

    jiaoben

    import gmpy2
    n=2279269
    p1=137
    p2=131
    p3=127
    e=19
    c=768924
    dp1=gmpy2.invert(e,p1-1)
    dp2=gmpy2.invert(e,p2-1)
    dp3=gmpy2.invert(e,p3-1)
    m1=pow(c,dp1,p1)
    m2=pow(c,dp2,p2)
    m3=pow(c,dp3,p3)
    qInv1=gmpy2.invert(p2,p1)
    h1=(qInv1*((m1-m2) % p1)) % p1
    m4=m2+h1*p2
    p4=p1*p2
    qInv2=gmpy2.invert(p3,p4)
    h2=(qInv2*((m4-m3)% p4)) % p4
    m=m3+h2*p3
    print(m)
    

    dp、dq泄露

    dp = d mod p-1
    dq = d mod q-1

    这种参数是为了让解密的时候更快速而产生的

    已知p,q,dp,dq,c求m

    import gmpy2
    import binascii
    import libnum
    def decrypt(dp,dq,p,q,c):
        InvQ = gmpy2.invert(q,p)
        mp = pow(c,dp,p)
        mq = pow(c,dq,q)                   #求幂取模运算
        m=(((mp-mq)*InvQ)%p)*q+mq          #求明文公式
        print (binascii.unhexlify(hex(m)[2:]))
        print(libnum.n2s(m))
    p = 
    q = 
    dp = 
    dq = 
    c = 
    decrypt(dp,dq,p,q,c)
    
    

    已知e,n,dp,c求m

    import gmpy2
    import libnum
    import binascii
    def getd(n,e,dp):
        for i in range(1,e):            #在范围(1,e)之间进行遍历
            if (dp*e-1)%i == 0:
                if n%(((dp*e-1)/i)+1)==0:    #存在p,使得n能被p整除
                    p=((dp*e-1)/i)+1
                    q=n/(((dp*e-1)/i)+1)
                    phi = (p-1)*(q-1)         #欧拉定理
                    d = gmpy2.invert(e,phi)%phi        #求模逆
                    return d
    e =
    n = 
    dp = 
    c = 
    d=getd(n,e,dp)
    m=pow(c,d,n)                            #快速求幂取模运算
    print(binascii.unhexlify(hex(m)[2:]))       #16进制转文本
    print(libnum.n2s(m))
    
    

    https://blog.csdn.net/qq_42939527/article/details/105202716


    已知n,r求p,q

    核心是通过n和r解出p和q

    1.二分法,求得p,q
    
    2.RSATool2v17中,输入p,q,r,e,得到d  (脚本也可)
    
    3.通过m=pow(c,d,n)
    注意:有时题目有要求,解出的可能是m乘上某一个参数,这是需要仔细审题
    
    4.转字符,得到flag
    
    

    脚本

    import gmpy2
    import numpy as np
    np.set_printoptions(suppress=True)
    
    n=gmpy2.mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199...)
    r=gmpy2.mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199...)
    
    c1=n-r+1
    print (c1)
    
    l=c1/2
    r=c1
    #p=(l+r)/2
    #y=p*(c1-p)
    
    while l<r:
    	p=(l+r)/2
    	y=p*(c1-p)
    	if y==n:
    		print (p)
    		break
    	if y>n:
    		print (y>n)
    		l=p
    	else:
    		print (y<n)
    		r=p
    		print ("done")
    q=c1-p
    print q
    /*
    if p>q:
        p,q=q,p
    factor2 = 2021 * p + 2020 * q
    if factor2 < 0:
        factor2 = (-1) * factor2
        
    _P=sympy.nextprime(factor2)
    */
    
    
  • 相关阅读:
    Springboot整合dubbo搭建基本的消费、提供和负载均衡
    SpringBoot与Dubbo整合的三种方式
    Dubbo整合SpringBoot
    Java 如何有效地避免OOM:善于利用软引用和弱引用
    finalize的作用
    垃圾回收
    不同JDK版本之间的intern()方法的区别-JDK6 VS JDK6+
    Java8内存模型—永久代(PermGen)和元空间(Metaspace)
    to meet you
    Atomic long 和long的区别
  • 原文地址:https://www.cnblogs.com/NPFS/p/13383625.html
Copyright © 2011-2022 走看看