花了将近半天的时间学完了离散数学的第十一章,在这里做一下常见的rsa的分析。
rsa相关理论
首先在进行rsa加密之前会申请两个大素数,分别为p、q(一般会用getprime生成),计
1 | n = p * q |
根据欧拉函数求得
1 | phi(n) = phi(p) * phi(q) = (p - 1) * (q - 1) |
取一小于phi(n)的整数e作为加密的指数,这里的e在原则上要求与phi(n)互质,但是也会有遇到e与phi(n)不互质的情况,这里在之后会继续讲到。根据改e求出关于phi(n)的模反元素d,满足以下关系:
1 | d * e ≡ 1 (mod phi(n)) |
注意上述的符号“≡”为同余符号,意义为左右两边的余数相同。
随后销毁p、q,这时(n,e)为公钥,(n,d)为私钥。
信息加密
在信息加密中,使用公钥对信息m进行加密,加密之后的密文c为:
1 | c ≡ pow(m,e,n) |
上式中的pow意思为指数并取余,等价于
1 | c ≡ (m ** e) mod n |
信息解密
在信息解密中,使用私钥对密文c进行解密,解密之后的明文m为:
1 | m ≡ pow(c,d,n) |
等价于:
1 | m ≡ (c ** d) mod n |
正确性证明
针对以上加密解密的是否正确,对齐进行证明:
1 | 已知: |
证明完了rsa的正确性,接下来介绍以下常见的rsa攻击,以及获取m的exp模板
rsa攻击
已知p、q、n、e,求m
1 | import gmpy2 |
n较小可直接进行分解
使用yafu进行整数分解
exp同上
n、e较大时使用RsaCtfTool进行攻击
exp同上
已知p、q、dp、dq
推导过程:
1 | n = p * q |
1 | import gmpy2 |
已知dp、n、e
由于已经知道了e,所以只要知道p、q就可以对m进行求解,推导过程如下
1 | c ≡ (m ** e) mod n |
1 | import gmpy2 |
已知n、e、d,求p、q
推导过程:
1 | n = p * q |
1 | import gmpy2 |
n分解出不止两个因数
这里其实考察的是欧拉公式的本质
1 | n = p1 * p2 * p3 |
接下来的步骤就和已知p、q求m一样
低加密指数分解攻击
在加密过程中,对e选择可以说是任意的,但是如果e的取值太小,会造成安全问题,下面分别对e=2和e=3进行讨论
e=2
当e=2时,在进行加密的时候,相当于只是对明文进行了平方处理,只需要对密文进行开方或者加上几个n即可
1 | import gmpy2 |
e=3
当e为3的时候,加密过程也就是对明文进行立方处理,可能会大于n,那就需要进行爆破n的个数:
1 | import gmpy2 |
共模攻击
使用相同的模数 N 、不同的私钥,加密同一明文消息,即对同一个明文m,使用不同的e对其进行加密,这里需要用到的数学理论为扩展欧几里得算法:
1 | c1 ≡ (m ** e1) mod n |
1 | from Crypto.Util.number import getPrime, long_to_bytes |
e与phi不互素
这种情况为当我们顺利的求出p、q之后,结果计算模反元素时发现e与phi(n)不互素
m ** (gcd(e,phi(n))) < n
这种情况比较容易理解,和低加密指数分解类似:
1 | n = p * q |
e ≠ 2且e | phi(n)
在这里,e不仅与phi(n)不互素,e反而成了phi(n)的一个因数,可以利用amm算法进行实现。
amm算法的python实现,但是需要用sagemath跑脚本
1 | import random |
例题已知p、n、e,对flag进行筛选:
1 | c = 15433214846771804225704093824935372144929516863829752998270111032551363583267576397009018518790803908369965458162930857063271509296349091229352855725285388975497906340053281554202527432848881160125418406408621879995822551367228501163128699032015069585502994319524445505522625561831240862136447585120010288772692097621553249775117843166714346924868089146429002417223863834435968726551668931140147337199939823985783939085842479154589529244209712172799274024573845157268545992888944742377166586536479490962335287124809557709167220756920767331929168230518135523463578566851417486746667008938122693256033127001185017237773 |
已知n、p的前几位
这种题目一般来说会对p做一个移位的处理,例如下面这个题:
1 | from Crypto.Util.number import * |
我们发现这边p的后400位都被置为了0,那也有办法对p进行泄露,脚本同样需要用sage跑:
1 | n = 18938788289620067144478923765173436262311068999676893390445049546312684365514536608080428689027531397349166856558418856679224420742579451021100821630696237144618222613228409489544352235694744341844439195591698481080058837746671025995110356482183267906661682973204274675740778215117419822965350657167064853267563622675079821715407856685970086113198725477878846760181691806710341138916872837282000986249555478412089316527688906861692832058293518550563719385564121119900966641174901328361905085250758567429953313748419974818256075740638622748678830559125695986550219947994604631541361313204232163980034059284851019250771 |