RSA算法

RSA算法

1 前置知识

1.1 扩展欧几里得算法

贝祖定理:如果有a、b是整数,那么一定存在整数x、y使得
a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

也就是说,如果 a x + b y = m ax+by=m ax+by=m有解,那么m一定是gcd(a, b)的倍数,要想求出gcd(a, b)显然可以使用辗转相除法,下面是辗转相除法的Python代码:

def gcd(a, b):
  if b == 0:
      return a
  else:
      return gcd(b, a%b)

但是,对于 a x + b y = m ax+by=m ax+by=m,我们不但想知道其是否有解,并且想要求解出答案。所以,使用扩展欧几里得算法

在到达递归边界的时候, b = 0 , a = g c d ( a , b ) b=0,a=gcd(a, b) b=0a=gcd(a,b)这时可以观察出来一个式子的一个解: a ∗ 1 + b ∗ 0 = g c d ( a , b ) , x = 1 , y = 0 a*1+b*0=gcd(a, b), x=1, y=0 a1+b0=gcd(a,b),x=1,y=0,注意这时的a,b已经不是最开始的a,b,所以我们要反推开始的状态。因为这是递归算法,也就是说,如果我们知道了这一层和上一层的关系,一层一层的反推回去,就可以退回最初的情况。

假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得
b ∗ x 1 + ( a % b ) ∗ y 1 = g c d b*x_1+(a\%b)*y_1=gcd bx1+(a%b)y1=gcd
首先我们知道 a % b = a − ( a / / b ) ∗ b a\%b=a-(a//b)*b a%b=a(a//b)b,带入上述式子:
b ∗ x 1 + ( a − ( a / / b ) ∗ b ) ∗ y 1 = b ∗ x 1 + a ∗ y 1 − ( a / / b ) ∗ b ) ∗ y 1 = a ∗ y 1 + b ∗ ( x 1 − a / / b ∗ y 1 ) = g c d b*x_1 + (a-(a//b)*b)*y_1 \\ =b*x_1 + a*y_1-(a//b)*b)*y_1 \\ =a*y_1 + b*(x_1- a//b*y_1) =gcd bx1+(a(a//b)b)y1=bx1+ay1(a//b)b)y1=ay1+b(x1a//by1)=gcd
发现 x = y 1 , y = x 1 − a / / b ∗ y 1 x=y_1, y = x_1-a//b*y_1 x=y1,y=x1a//by1,这样就得到了相邻状态的x和y的变化,就可以反推出x和y的值了。这样可以求解出答案,但是得到的x可能为负值或者x并不是最小正整数解。

对于 a x + b y = m ax+by=m ax+by=m,我们知道a,b的最小公倍数等于 ( a b ) / g c d ( a , b ) (ab)/gcd(a, b) (ab)/gcd(a,b) ,那么当
x = x + b / g c d ( a , b ) y = y − a / g c d ( a , b ) 带入原式 = > a ( x + b / g c d ( a , b ) ) + b ( y − a / g c d ( a , b ) ) x = x+b/gcd(a, b) \\ y = y-a/gcd(a,b) \\ 带入原式=> a(x+b/gcd(a, b))+b(y-a/gcd(a,b)) x=x+b/gcd(a,b)y=ya/gcd(a,b)带入原式=>a(x+b/gcd(a,b))+b(ya/gcd(a,b))
可以看出来原式的值不变,也就是说x、y的变化周期T为 b / g c d ( a , b ) 、 a / g c d ( a , b ) b/gcd(a, b)、a/gcd(a,b) b/gcd(a,b)a/gcd(a,b),由此我们就可以使用x = (x+T)%T得到最小正整数解。另外值得一提的是,在使用扩展欧几里得算法求解逆元时(如下述代码),由于输入的a、p为互质的两个数字,故 g c d ( a , p ) = 1 gcd(a, p)=1 gcd(a,p)=1,此时x的周期 T = p / g c d ( a , p ) = p T=p/gcd(a,p)=p T=p/gcd(a,p)=p

def exgcd(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, q = exgcd(b , a%b)
        x, y = y, (x - (a//b)*y)
        return x, y, q
      
# 使用扩展欧几里得算法求解逆元
def ModReverse(a,p):
    x, y, q = exgcd(a,p)
    if q != 1:
        raise Exception("No solution.")
    else:
        return (x + p) % p #防止负数

2 密钥生成步骤

Alice要和Bob通信,以下是她通过RSA算法生成公钥和私钥的步骤:

  1. 选择两个不相等的质数p、q。

    此处,Alice选择61和53。

    在实际情况中,p和q越大就越难破解。

  2. 计算p、q乘积n的欧拉函数,因为p和q都是质数,所以 Φ ( n ) \Phi(n) Φ(n)可以通过下述公式计算:
    Φ ( n ) = Φ ( p q ) = Φ ( p ) Φ ( q ) = ( p − 1 ) ( q − 1 ) \Phi(n) = \Phi(pq) = \Phi(p)\Phi(q) = (p-1)(q-1) Φ(n)=Φ(pq)=Φ(p)Φ(q)=(p1)(q1)
    Alice计算出 Φ ( n ) = Φ ( 3233 ) = 3120 \Phi(n) = \Phi(3233) = 3120 Φ(n)=Φ(3233)=3120

  3. 1 < e < Φ ( n ) 1<e<\Phi(n) 1<e<Φ(n)之间随机选择整数e,并且e和 Φ ( n ) \Phi(n) Φ(n)互质。

    Alice选择了17。

    在实际应用中,常常选择65537。

  4. 计算e对于 Φ ( n ) \Phi(n) Φ(n)逆元d,即求解
    e d ≡ 1 ( m o d Φ ( n ) ) ed \equiv1(mod\Phi(n)) ed1(modΦ(n))
    Alice使用扩展辗转相除法求解出逆元d=2753.

  5. 将n和e封装为公钥,n和d封装为私钥

    对于Alice来说(3233,17)是公钥,而(3233,2753)是私钥。

    在实际应用中,公钥和私钥都需要用ASN.1格式来表达

3 RSA算法的可靠性

从上述的步骤可以看出,“n和e封装为公钥,n和d封装为私钥。”,所以**“有无可能在已知n和e的情况下,推导出d?”**,就是对RSA算法可靠性的探讨。

我们知道,如果我们知道 Φ ( n ) \Phi(n) Φ(n)就能求解出d;而 Φ ( n ) = ( p − 1 ) ( q − 1 ) \Phi(n) = (p-1)(q-1) Φ(n)=(p1)(q1),也就是说得知p、q就可以计算d。也就是说,如果n可以被因数分解,那么d就可以被算出,也就意味着私钥被破解。但是,大整数的因数分解是一件很困难的事情,目前除暴力破解之外没有发现别的有效方法。

对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的

4 RSA算法的加密和解密

4.1 RSA加密需要使用公钥(n ,e)

假设Bob要向Alice发送加密信息m,他就要使用Alice的公钥(n, e)对m进行加密,这里的m必须为整数(如果是字符的话可以取ascii值或者unicode值),且m必须小于n。

下式中c就是m加密后的值:
m e ≡ c ( m o d   n ) m^e\equiv c(mod\ n) mec(mod n)

假设Alice的公钥是(3233, 17),Bob的m假设为65,那么可以计算出
6 5 17 ≡ 2790 ( m o d   3233 ) 65^{17}\equiv 2790(mod\ 3233) 65172790(mod 3233)
于是,c=2790,Bob就把2790发送给Alice。

4.2 RSA解密需要使用私钥(n, d)

Alice在得知Bob发送来的c之后,通过下面的等式就可以求解出m:
c d ≡ m ( m o d   n ) c^d \equiv m(mod\ n) cdm(mod n)

c=2790,私钥是(3233, 2753),那么Alice计算出:
279 0 2753 ≡ 65 ( m o d   3233 ) 2790^{2753}\equiv 65(mod\ 3233) 2790275365(mod 3233)
至此,Alice就可以知道Bob加密之前的原文是65.

5 题目

求明文:

pubkey.pem

flag.enc

openssl rsa -pubin -text -modulus -in pubkey.pem

运行结果:

Public-Key: (256 bit)
Exponent: 65537 (0x10001)
Modulus=C2636AE5C3D8E43FFB97AB09028F1AAC6C0BF6CD3D70EBCA281BFFE97FBE30DD
writing RSA key
-----BEGIN PUBLIC KEY-----
MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMJjauXD2OQ/+5erCQKPGqxsC/bNPXDr
yigb/+l/vjDdAgMBAAE=
-----END PUBLIC KEY-----

pip3 install pycryptodome

/Users/apple/Desktop/HelloWorld/Python_Main/venv/lib/python3.9/site-packages下的crypto的首字母改为大写,最后是/Users/apple/Desktop/HelloWorld/Python_Main/venv/lib/python3.9/site-packages/Crypto

# rsa_test.py
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse

def Gcd(a, b):
  if b == 0:
      return a
  else:
      return Gcd(b, a%b)

def Exgcd(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, q = Exgcd(b, a % b)
        x, y = y, (x - (a // b) * y)
        return x, y, q

def ModReverse(a,p):
    x, y, q = Exgcd(a,p)
    if q != 1:
        raise Exception("No solution.")
    else:
        return (x + p) % p #防止负数

# arsa = RSA.generate(1024)
# arsa.p = p
# arsa.q = q
# arsa.e = e
# arsa.n = arsa.q * arsa.p
# arsa.d = d

n = int('C2636AE5C3D8E43FFB97AB09028F1AAC6C0BF6CD3D70EBCA281BFFE97FBE30DD', 16)
# http://factordb.com
p = 275127860351348928173285174381581152299
q = 319576316814478949870590164193048041239
e = 65537
Phi_n = (p-1)*(q-1)
d = ModReverse(e, Phi_n)
u = inverse(p, q)
private_key = open('private_key.pem', 'wb')
arsa = RSA.RsaKey(n=n, e=e, d=d, p=p, q=q, u=u)
private_key.write(arsa.exportKey())
private_key.close()

这个地方原本的代码是RSA.generate(1024),但是如果这样在对p赋值时,会报错:

Traceback (most recent call last):
File “/Users/apple/Desktop/HelloWorld/Python_Main/rsa_test.py”, line 35, in
arsa.p = p
AttributeError: can’t set attribute

在翻阅generate(1024)的源码后,发现函数末尾是这样的:

    if p > q:
        p, q = q, p

    u = p.inverse(q)

    return RsaKey(n=n, e=e, d=d, p=p, q=q, u=u)

发现相比较RSA算法中的参数,多出参数u,是经过inverse(q, p)计算出来的,inverse()函数可以从from Crypto.Util.number import inverse引入,然后查看Rsakey对象的__init__()的输入参数:

class RsaKey(object):
    r"""Class defining an actual RSA key.
    Do not instantiate directly.
    Use :func:`generate`, :func:`construct` or :func:`import_key` instead.

    :ivar n: RSA modulus
    :vartype n: integer

    :ivar e: RSA public exponent
    :vartype e: integer

    :ivar d: RSA private exponent
    :vartype d: integer

    :ivar p: First factor of the RSA modulus
    :vartype p: integer

    :ivar q: Second factor of the RSA modulus
    :vartype q: integer

    :ivar u: Chinese remainder component (:math:`p^{-1} \text{mod } q`)
    :vartype u: integer

    :undocumented: exportKey, publickey
    """

    def __init__(self, **kwargs):
        """Build an RSA key.

        :Keywords:
          n : integer
            The modulus.
          e : integer
            The public exponent.
          d : integer
            The private exponent. Only required for private keys.
          p : integer
            The first factor of the modulus. Only required for private keys.
          q : integer
            The second factor of the modulus. Only required for private keys.
          u : integer
            The CRT coefficient (inverse of p modulo q). Only required for
            private keys.
        """

        input_set = set(kwargs.keys())
        public_set = set(('n', 'e'))
        private_set = public_set | set(('p', 'q', 'd', 'u'))
        if input_set not in (private_set, public_set):
            raise ValueError("Some RSA components are missing")
        for component, value in kwargs.items():
            setattr(self, "_" + component, value)
        if input_set == private_set:
            self._dp = self._d % (self._p - 1)  # = (e⁻¹) mod (p-1)
            self._dq = self._d % (self._q - 1)  # = (e⁻¹) mod (q-1)

发现可以不使用generate(),直接计算出相应的参数,初始化RsaKey对象,所以写出上述rsa_test.py

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

PCTF{256b_i5_m3dium}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值