RSA学习笔记:如何快速理解RSA加密算法
1. 前言
网上关于RSA的文章已经有很多很多了,包括实现的代码等。
但很少有文章讲述如何快速理解并回答有关于RSA学习或考试的相关问题。
由于我近期在学习一门课程,是墨大的COMP90043 Cryptography and Security,其中有很多RSA相关的题目,在这里想简单总结一下答题心得,希望可以给有需要的人一些帮助吧。
2. RSA算法原理
RSA其实非常简单,其原理就是非对称加密,举个简单的例子:
小明想发个消息M给小亮,又怕别人偷看,小亮就生成2个密钥,一个公钥(public key)一个私钥(private key)。
小亮先将公钥发送给小明,小明收到公钥后用公钥将信息加密成C,加密过后的密文C返回给小亮,小亮接收到密文C后再利用私钥将信息解密为M’,也就是解密后的信息,M和M’必须是对等的。
是不是比课堂上老师讲得清楚很多?嘿嘿,主要是现在网课时期,加上我们墨大教密码学的Udaya教授的英文口音实在太重…导致这门课我上得十分痛苦 =-=
2.1. 具体算法实现步骤
一般作业首先会给我们两个质数(two primes),也叫素数。对于需要复习质数方面知识的,可以看这个视频,讲得非常清晰。
利用这两个质数p 和q获取密钥,我们需要做的其实就只有五个步骤:
- 获取质数p和q
- p * q算出n
- 算出欧拉函数(Euler’s totient function),也就是:ϕ(n) = (p - 1) * (q - 1)
- 求出公钥e。这一步有一点复杂,也是所有文章中最含糊其辞的一个部分,我决定重点讲一下这个e。首先e需要大于1并且小于phi(n),也就是1 < e < ϕ*(n)。在这个条件下呢,e还必须和phi(n)互质(解释一下phi(n)就是ϕ(n))。在后面的例子中我也会谈到如何判断这两个数是不是互质。顺便一提,一般我们的作业题目都会说找一个最小的公钥(smallest possible exponent for the public key),即使作业没提到,正常情况下找最小公钥(Smallest (non-trivial) e)就好了,其他文章在这一步一般都会说:“随便找一个和phi(n)互质的e”,每次读到这里我人都傻了,在我印象数学不可能这么不严谨吧…
- 最后一步我们需要求出私钥d,这一步其实比求公钥还要复杂一些,教科书上说的是e × d ≡ 1 mod ϕ(n)。说人话就是e * d 取余 ϕ(n) = 1。有了这些,我们就可以得出私钥d了。
拥有密钥后,加密信息就更简单了:
首先需要传输的消息一般是用大写M表示,加密后的密文是C,解密了的明文是M’,所以M == M’。因为你解密了肯定是你之前传输过来的消息。知道这些后我们来加解密消息:
加密:Me mod n = C
解密:Cd mod n = M’
2.2. 简单示例
好!大致逻辑讲完后,我找个例子示范一下:
借用我们第六周tutorial课件的第四题,首先我们假设两个质数43 and 47,并且我们要传递消息M = 313。
- p = 43; q = 47
- n = p x q = 2021
- ϕ(n) = (p - 1) x (q - 1) = 1932
- e = gcd(e, ϕ(n)) = gcd(e, 1932) = 5 (如果没看懂我后面的代码示例会给出解释)
- d = ed mod ϕ(n) = 1; = 5 x d mod 1932 = 1; d = 773 (同样没看懂我后面会解释)
现在我们得出e = 5,d = 773, n = 2021,接下来我们开始加解密信息: - C = Me mod n = 3135 % 2021 = 464
- M’ = Cd mod n = 464773 % 2021 = 313
大功告成。
关于获取公钥e和私钥d的部分其实有一点复杂难懂,对于数学基础好的同学到这里可能就已经很清楚了。但如果像我一样数学不好,可能到这里还是有点一头雾水,那么接下来我们就用代码实操一下,因为关于获取公钥和私钥,使用代码会方便很多,并且也更容易帮助大家捋清思路,手算基本不太现实。
2.3. 代码示例
下面我会用python代码演示如何一步步得出结果,使用到的工具是jupyter。
(如果你不知道jupyter是什么,我十分推荐你去搜索相关的内容,jupyter可以将代码以块的形式运行,对于这种数学运算帮助非常大,你可以在过程中知道每一步的结果。当然,不使用jupyter甚至不使用python一样可以得出结果,所以问题不大。)
还是用上面的例子:
p = 43
q = 47
n = p * q
print(n)
# 输出结果为:2021
phin = (p - 1) * (q - 1)
print(phin)
# 输出结果为:1932
至此,我们拥有了p,q,n和phi(n)。
关于如何得出e,首先我们需要它和phi(n)互质,如何得知两个数是否互质,我们只需要知道他们的最大公约数(Greatest Common Divisor(GCD))等于1就好了。
在这里我会使用欧几里德算法得出e:
# gcd
def gcd(x,y):
if x<y:
x,y=y,x # 翻转它们的位置,永远让第一个数大于第二个数。
t = x%y # 先对两个数取一次余。
while t!=0: # 一直对x和y取余,取到0为止。
x,y=y,t
t=x%y
return y #返回上一次取余的结果,那个数就是它们的最大公约数。
# 使用gcd找出公钥e
def find_e(phin):
i = 2 # e要大于1,小于phin,所以取2
while True:
# 在i和1932之间找最大公约数,
# 如果最大公约数不是1就一直循环,利用这个伪死循环,计算机可以很快找出一个最小的公钥e
if gcd(i, phin) == 1:
break
i+=1
return i
e = find_e(phin)
print(e)
# 输出结果为:5
有代码和注释思路是不是就很清晰了。如果还是不太明白,我推荐你在用纸和笔模拟代码运行,自己去运算一次,反正输出的结果是5,你只需要计算4次能找到答案了。
计算d其实是比较复杂的,在这里我先给出一个gmpy2库的解决方案,非常优雅:
import gmpy2
d = gmpy2.invert(e,phin)
print(d)
# 输出结果为:773
就是这么简单,输出结果还非常快。当然,如果想要很好地去理解私钥d是如何产生的,我当时的思考方案是穷举法:
# calculate d
d = 1
while True:
# 如果e*d mod phi(n) = 1,这个d就是我们要找的私钥, 没找到就一直循环下去。
if e * d % phin == 1:
break
d+=1
if d >= phin: # 如果d超出phi(n)的范围了,那么这个算式就是有问题的。
print("out of the value")
break
print(d)
# 输出结果为:773
其实一开始我对我这个方案挺满意的,直到某一次作业教授给出了一个将近100位数的phi(n)。我的电脑用上面这个代码跑了368分钟没跑出结果…最后我用gmpy2.invert,0.4秒就出结果了,真的裂开。
后面加密解密就很简单了:
m = 313 # 需要加密的明文
c = pow(m,e,n) # m^e mod n
print(c)
# 输出结果为:464
dm = pow(c,d,n) # c^d mod n
print(dm)
# 输出结果为:313
3. 结尾
本文中关于RSA加密算法其实只谈论了非常简单的基础知识,并且只是给出了一个解题的思路。但也基本属于胎教级教程了。RSA还是非常值得去深入研究的,包括签名验证等等。其实python有生成RSA密钥的库import rsa
,感兴趣的可以自己去搜索一下相关的知识,我下面也附上了一个链接,在这里就不过多介绍了。
我也正在学习密码学相关的知识,谈论的比较浅薄,如果有什么地方写得不好或者有错误,欢迎指正。
4. 扩展阅读
如果还是对RSA感到困惑,或者觉得我讲的不是很透彻的话,这里我附上几个参考资料:
RSA算法原理(一):http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
银行密码系统安全吗?质数(素数)到底有啥用?李永乐老师11分钟讲RSA加密算法(2018最新):https://www.bilibili.com/video/BV1Ts411H7u9/?spm_id_from=333.999.0.0&vd_source=b72e73622847cc3b5219f60be44bf555
公钥密码之RSA:https://www.cnblogs.com/clwsec/p/15680029.html
python的rsa库的使用:https://blog.csdn.net/qq_41621362/article/details/102105642