RSA中的共模攻击和例题讲解

0x01 利用条件

​ 两个用户使用相同的模数 n、不同的私钥(加密指数e不相同)时,加密同一明文m消息,即(n,m)相同,(c,e)不同。且两个加密指数 e 1 e_1 e1 e 2 e_2 e2满足 g c d ( e 1 , e 2 ) = 1 gcd(e_1, e_2) = 1 gcd(e1,e2)=1,即 e 1 e_1 e1 e 2 e_2 e2互为素数

0x02 攻击原理

已知 e 1 e_1 e1, e 2 e_2 e2,且 g c d ( e 1 , e 2 ) = 1 gcd(e_1, e_2) = 1 gcd(e1,e2)=1, 由扩展欧几里得算法得,存在一组整数 s 1 s_1 s1, s 2 s_2 s2,使得:公式(1)
e 1 × s 1 + e 2 × s 2 = g c d ( e 1 , e 2 ) = 1 e_1 \times s_1 + e_2 \times s_2 = gcd(e_1, e_2) = 1 e1×s1+e2×s2=gcd(e1,e2)=1
又由RSA算法原理可得:公式(2)
{ c 1 ≡ m e 1   (   m o d   n ) c 2 ≡ m e 2   (   m o d   n ) \begin{cases} c_1 \equiv m^{e_1}\ (\bmod n)\\\\ c_2 \equiv m^{e_2}\ (\bmod n) \end{cases} c1me1 (modn)c2me2 (modn)

求m的方法一:

由以上(1)(2)可得:
m ≡ m 1   (   m o d   n ) ≡ m e 1 × s 1 + e 2 × s 2   (   m o d   n ) ≡ m e 1 × s 1 × m e 2 × s 2   (   m o d   n ) ≡ [ ( m e 1 ) s 1   (   m o d   n ) ] × [ ( m e 2 ) s 2   (   m o d   n ) ] ≡ c 1 s 1   (   m o d   n ) × c 2 s 2   (   m o d   n ) \begin{align*} m &\equiv m^1\ (\bmod n)\\\\ &\equiv m^{e_1 \times s_1 + e_2 \times s_2}\ (\bmod n)\\\\ &\equiv m^{e_1 \times s_1} \times m^{e_2 \times s_2}\ (\bmod n)\\\\ &\equiv [(m^{e_1})^{s_1}\ (\bmod n)]\times [(m^{e_2})^{s_2}\ (\bmod n)]\\\\ &\equiv c_1^{s_1}\ (\bmod n) \times c_2^{s_2}\ (\bmod n) \end{align*} mm1 (modn)me1×s1+e2×s2 (modn)me1×s1×me2×s2 (modn)[(me1)s1 (modn)]×[(me2)s2 (modn)]c1s1 (modn)×c2s2 (modn)
由于 c 1 , c 2 , s 1 , s 2 c_1,c_2,s_1,s_2 c1,c2,s1,s2都是已知的,所以可求出m的值。

即:公式(3)
m ≡ c 1 s 1   (   m o d   n ) × c 2 s 2   (   m o d   n ) m \equiv c_1^{s_1}\ (\bmod n) \times c_2^{s_2}\ (\bmod n) mc1s1 (modn)×c2s2 (modn)

求m的方法二:

或者由(1)(2)可得:
c 1 s 1 c 2 s 2 ≡ m s 1 e 1 m s 2 e 2   (   m o d   n ) ≡ m ( s 1 e 1 + s 2 e 2 )   (   m o d   n ) ≡ m   (   m o d   n ) \begin{align*} c_{1}^{s_1}c_{2}^{s_2} &\equiv m^{s_1e_1}m^{s_2e_2}\ (\bmod n)\\\\ &\equiv m^{(s_1e_1+s_2e_2)}\ (\bmod n)\\\\ &\equiv m\ (\bmod n) \end{align*} c1s1c2s2ms1e1ms2e2 (modn)m(s1e1+s2e2) (modn)m (modn)

即:公式(4)
c 1 s 1 c 2 s 2 ≡ m   (   m o d   n ) c_{1}^{s_1}c_{2}^{s_2} \equiv m\ (\bmod n) c1s1c2s2m (modn)


中间还有两个地方需要注意一下。

一是 s 1 s_1 s1, s 2 s_2 s2的值有一个为负值,在数论中,求一个数 a a a n n n − m -m m次幂,等于这个数 a a a n n n逆元的 m m m次幂。

即有公式(5)
n − m ≡ ( n − 1 ) m   (   m o d   n ) n^{-m} \equiv (n^{-1})^m\ (\bmod n) nm(n1)m (modn)

0x03 例题分析-1

题目代码如下:

题目已在青少年CTF训练平台上架:

https://www.qsnctf.com/challenges#actually_ezrsa2-316

import gmpy2
from Crypto.Util.number import *
import uuid
flag = b"flag{"+str(uuid.uuid4()).encode()+b"}"
m = bytes_to_long(flag)
p = getStrongPrime(1024)
q = getStrongPrime(1024)
n1 = p * q
n2 = p * q
e1 = 2333
e2 = 0x2333
assert GCD(e1,e2)==1
c1 = pow(m, e1, n1)
c2 = pow(m, e2, n2)
print("n1 =",n1)
print("n2 =",n2)
print("e1 =",e1)
print("e2 =",e2)
print("c1 =",c1)
print("c2 =",c2)
'''
n1 = 25977434245383213061502496098095465491298593274819892066680142309644604447122213990193649136640792045616043244452359563096681308111786639428720577613208873073208345373399185801097504934529486981943138422062519093084571490989920108900165063768027454172383479340269594705161727160665663784155005546900956290823300194993982080817820176683256899248006819975050544361436011973694569861482116060287819754562815300267132104496249550156359417476864266926873511338883992689905817979797654219916643111997131400074229578490338679081091020589498111095229814729232804557730207461328006855958086611451644501518923369121332282446387
n2 = 25977434245383213061502496098095465491298593274819892066680142309644604447122213990193649136640792045616043244452359563096681308111786639428720577613208873073208345373399185801097504934529486981943138422062519093084571490989920108900165063768027454172383479340269594705161727160665663784155005546900956290823300194993982080817820176683256899248006819975050544361436011973694569861482116060287819754562815300267132104496249550156359417476864266926873511338883992689905817979797654219916643111997131400074229578490338679081091020589498111095229814729232804557730207461328006855958086611451644501518923369121332282446387
e1 = 2333
e2 = 0x2333
c1 = 18485676710986119075173536683686462950263457469331993885761169971214128938032438063231897522845618680375729397028835700363169782372594587099048372908212345790811034629235715272689844039455428850999889426940076877552005174718119690829981655533087526118363975468542716361201641751206611729446725223668335826884543868459078761494737060952796969364730619523110915396112202826020259561117145528032775008508084399606014601141698178821709270288546823847725591199659655029049623145505102599794072348396882393484944750435097930829667964115769696899896453970102851438725866601300114175722160091587885920442620806302225997894178
c2 = 16695364664928581532800627548063845285532382094248770791332184106311111465138692453038543176627867352470844455051476759173301213222665138578557853047169090740483130873450630219045786462114849315733985964814297323071258057067338599104261584932140969238868725551762047861055560927674158814431522126509606052082877955899865835628409554581388612342235123760360863332450664084011638284740583896249409211202260177051437402683756328647008719984689105698475757077859180489424704827694801729848236720022267059771816763723353599632989669237797090397186374989429083807476909642056936466992945632706494532552043613399250805214165
'''

在利用扩展欧几里得算法之后,注意判断 s 1 s_1 s1 s 2 s_2 s2的正负情况,然后根据公式(4)或者公式(5)即可求得明文,解题代码如下:

import gmpy2
from Crypto.Util.number import *
n1 = 25977434245383213061502496098095465491298593274819892066680142309644604447122213990193649136640792045616043244452359563096681308111786639428720577613208873073208345373399185801097504934529486981943138422062519093084571490989920108900165063768027454172383479340269594705161727160665663784155005546900956290823300194993982080817820176683256899248006819975050544361436011973694569861482116060287819754562815300267132104496249550156359417476864266926873511338883992689905817979797654219916643111997131400074229578490338679081091020589498111095229814729232804557730207461328006855958086611451644501518923369121332282446387
n2 = 25977434245383213061502496098095465491298593274819892066680142309644604447122213990193649136640792045616043244452359563096681308111786639428720577613208873073208345373399185801097504934529486981943138422062519093084571490989920108900165063768027454172383479340269594705161727160665663784155005546900956290823300194993982080817820176683256899248006819975050544361436011973694569861482116060287819754562815300267132104496249550156359417476864266926873511338883992689905817979797654219916643111997131400074229578490338679081091020589498111095229814729232804557730207461328006855958086611451644501518923369121332282446387
e1 = 2333
e2 = 0x2333
c1 = 18485676710986119075173536683686462950263457469331993885761169971214128938032438063231897522845618680375729397028835700363169782372594587099048372908212345790811034629235715272689844039455428850999889426940076877552005174718119690829981655533087526118363975468542716361201641751206611729446725223668335826884543868459078761494737060952796969364730619523110915396112202826020259561117145528032775008508084399606014601141698178821709270288546823847725591199659655029049623145505102599794072348396882393484944750435097930829667964115769696899896453970102851438725866601300114175722160091587885920442620806302225997894178
c2 = 16695364664928581532800627548063845285532382094248770791332184106311111465138692453038543176627867352470844455051476759173301213222665138578557853047169090740483130873450630219045786462114849315733985964814297323071258057067338599104261584932140969238868725551762047861055560927674158814431522126509606052082877955899865835628409554581388612342235123760360863332450664084011638284740583896249409211202260177051437402683756328647008719984689105698475757077859180489424704827694801729848236720022267059771816763723353599632989669237797090397186374989429083807476909642056936466992945632706494532552043613399250805214165
'''区分s1和s2的正负'''
_, s1, s2 = gmpy2.gcdext(e1,e2)
if s1 < 0 :
    s1 = -s1
    c1 = gmpy2.invert(c1, n1)
if s2 < 0 :
    s2 = -s2
    c2 = gmpy2.invert(c2, n2)
'''带入公式(4)或公式(5)'''
m = pow(c1,s1,n2)*pow(c2,s2,n2) % n1
print(long_to_bytes(m))
'''输出:flag{d8f62d48-43fa-4939-ab70-56a16c577c62}'''

0x04 例题分析-2

题目描述:

{6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249,773}
{6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249,839}
message1=3453520592723443935451151545245025864232388871721682326408915024349804062041976702364728660682912396903968193981131553111537349
message2=5672818026816293344070119332536629619457163570036305296869053532293105379690793386019065754465292867769521736414170803238309535

可以看出两个公钥的 N 是一样的,并且两者的 e 互素。写一个脚本跑一下:

import gmpy2
n = 6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249
e1 = 773
e2 = 839
message1 = 3453520592723443935451151545245025864232388871721682326408915024349804062041976702364728660682912396903968193981131553111537349
message2 = 5672818026816293344070119332536629619457163570036305296869053532293105379690793386019065754465292867769521736414170803238309535
# s & t
gcd, s, t = gmpy2.gcdext(e1, e2)
if s < 0:
    s = -s
    message1 = gmpy2.invert(message1, n)
if t < 0:
    t = -t
    message2 = gmpy2.invert(message2, n)
plain = pow(message1, s, n) * pow(message2, t, n) % n
print(plain)

得到:

➜  python3 exp.py
1021089710312311910410111011910111610410511010710511610511511211111511510598108101125

这时候需要考虑当时明文是如何转化为这个数字了,一般来说是 16 进制转换,ASCII 字符转换,或者 Base64 解密。这个应该是 ASCII 字符转换,进而我们使用如下代码得到 flag

i = 0
flag = ""
plain = str(plain)
while i < len(plain):
    if plain[i] == '1':
        flag += chr(int(plain[i:i + 3]))
        i += 3
    else:
        flag += chr(int(plain[i:i + 2]))
        i += 2
print(flag)
'''输出:flag{whenwethinkitispossible}'''

这里之所以使用 1 来判断是否为三位长度,是因为 flag 一般都是明文字符,而 1 开头的长度为 1 或者 2 的数字,一般都是不可见字符。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值