RSA基础原理
数论:
互质:两个正整数只有一个公因数1时,则称其为互质。比如:6和7互质,而4和6不互质。 欧拉函数 φ(N) :小于或等于 N 的正整数中与 N 互质的数的数目。例如:N = 6 那么此数目为2,φ(6) = 2*3=(2-1)*(3-1)=2 这里为什么不是1*6呢?因为2和3是两个素数。 若 p 为素数,则 φ(p) = p-1 (因为每一个小于p的数(这里指与p互质的小于p的那些数)都与p互质) 又有若 N = p * q,则 φ(N) = φ(p) * φ(q),这里涉及到欧拉函数性质和计算方式,我们不在此深究,感兴趣的可以从N中有哪些和N不互质的数来进行推导本公式。 由此在RSA中,有φ(N) = (p-1) * (q-1) 乘法逆元: 在加法中,我们有a+(−a)=0,我们称其互为相反数。 在乘法中,我们有a⋅(1/a)=1,我们称其互为倒数。 在矩阵中,我们有M⋅M−1=E,我们称其为逆矩阵。 其实我们可以用一个统一的称呼:逆元,即某种运算下的逆元素,我们会发现,元素和其逆元素进行运算之后总是一个定值,实际上在代数中,他们构成了一个群(不用深究),我们只需要了解是在模意义下的乘法逆元即可。 在模 p 意义下,指的是后续的所有运算都是在模 p 的条件下满足,例如 3⋅4≠1 但 (3⋅4) mod 11=(1) mod 11,对于这种式子我们称其为同余式,并且有专门的同余符号进行表示: 3⋅4 ≡ 1(mod11) 所以参考上面乘法中的逆元运算规则,在模意义下则有: a⋅a−1 ≡ 1(mod p) 我们称 a 和 a−1 互为在模 p 意义下的乘法逆元。例如上述中的 3 与 4 互为在模 11 下的乘法逆元。
一、RSA原理
非对称加密
RSA算法是一种非对称加密算法,与对称加密算法不同的是,RSA算法有两个不同的密钥,一个是公钥,一个是私钥。
非对称加密算法有两种密钥:
私有密钥Sk(Secret key):又称私钥,只能是发送者自己保存。
1、乙方生成一对密钥(公钥和私钥)并将公钥向其它方公开。
2、得到该公钥的甲方使用该密钥对机密信息进行加密后再发送给乙方。
3、乙方再用自己未公开的私钥对加密后的信息进行解密。乙方只能用其专用密钥(私钥)解密由对应的公钥加密后的信息。
在传输过程中,即使攻击者截获了传输的密文,并得到了乙的公钥,也无法破解密文,因为只有乙的私钥才能解密密文。
同样,如果乙要回复加密信息给甲,那么需要甲先公布甲的公钥给乙用于加密,甲自己保存甲的私钥用于解密。
RSA
那怎么从0开始,进行一个RAS的加密呢?
首先按照加密方式,发送消息的一方,先
生成公私钥:
1、首先选取两个不同的大素数 p和q,然后计算 N = p * q 2、求欧拉函数值 φ(N) = φ(p) * φ(q) = (p-1) * (q-1) 3、选择一个小于 φ(N) 的整数 e,并且满足 e 和 φ(N) 互质,求得 e 在模 φ(N) 意义下的乘法逆元 d,有 e * d ≡ 1 (mod φ(N)) 4、销毁 p 和 q
那么 (N,e)是公钥,(N,d)是私钥
RSA算法本质上都是基于数学运算,在加密时我们需要先将消息转化为一个数字 m(例如消息ASCII码的二进制排列转为数字),然后有
c ≡ me(mod N)
此时得到的 c 便是我们的密文。
解密
加密时我们只用到了公钥 (N,e),同理解密时我们也只需用到私钥 (N,d)。有:m ≡ cd(mod N)
此时得到的 m 便是我们的明文消息。
m ≡ cd ≡ (me)d ≡ med(mod N) (在模相同情况下,同余式是可以如此转换的) 又有 ed ≡ 1(mod φ(N)),即ed = 1 + kφ(N)。
2、若gcd(m,N) ≠ 1,则 m 为 p 或 q 的整数,设 m = hp,有
med ≡ med−1 ≡ mk(p−1)(q−1)⋅m ≡ (1+xq)⋅m ≡ m + xq⋅hp ≡ m (mod N)
其中
mk(p−1)(q−1) = (mk(p−1))q−1 = 1 + xq
二、算法
python请使用3.8以上的版本,我自己用的是3.9.5版本
pycryptodome库
pip3 install pycryptodome # 或 python3 -m pip install pycryptodome
路径报错
pip install --target=路径(这里的路径不用担心,报错的时候系统会给出你路径的,只需要复制过来即可) pycryptodome
更新使用
pip install --upgrade --target=上面一样的路径 模块名
1.生成两个素数
from Crypto.Util.number import * p = getPrime(512) q = getPrime(512) print(p) print(q)
q和p是两个512位素数,这两个数可以返回一个n位的素数。
2. n = p * q,φ (n) = (p - 1) * (q - 1)
n = p * q
phi = (p - 1) * (q - 1)
3. ed ≡ 1(mod φ(n)) , e与phi互素
互素判断,e的乘法逆元
e = 65537 assert GCD(e, phi) == 1 #最大公因数,结果为1时,互素 d = inverse(e, phi)
1 | inverse(d, phi) = = e |
以上,(n,e)是公钥,(n,d)是私钥
加密:
m 明文:需要加密的消息
m = b‘Nineday’
这里密文是一个bytes类型的字符串,每个字符8位二进制储存,是字符串最原始的存储形式。
直接定义 m = "Nineday" ,这是一个Unicode的字符串,需要转换成bytes类型才能进行后续加密。
将字符串转换为数字,将密文转换为数字:
1 | m = bytes_to_long(m) |
加密
1 2 | c = pow (m, e, n) print (c) |
=> c = me % n
注意在代码中,不可以使用 直接的算式运算,pow函数可以快速得出结果,但是直接使用算式运算,会产生大量的运算。
此时,c就是密文:加密后的消息
解码:
1 | msg = pow (c, d, n) |
完整代码:
比较
from Crypto.Util.number import * p = getPrime(512) q = getPrime(512) n = p*q phi = (p-1)*(q-1) e = 65537 assert GCD(e, phi) == 1, "判断互素" d = inverse(e, phi) print(f'公钥:({e}, {n})') print(f'私钥:({d}, {n})') message = b'Nineday' m = bytes_to_long(message) print('消息:', m) c = pow(m, e, n) print('密文:', c) msg = pow(c, d, n) print('明文:', msg) assert msg == m, "是否一样"
基础解码代码:
import gmpy2 from Crypto.Util.number import * e = 65537 p = 12567387145159119014524309071236701639759988903138784984758783651292440613056150667165602473478042486784826835732833001151645545259394365039352263846276073 q = 12716692565364681652614824033831497167911028027478195947187437474380470205859949692107216740030921664273595734808349540612759651241456765149114895216695451 c = 108691165922055382844520116328228845767222921196922506468663428855093343772017986225285637996980678749662049989519029385165514816621011058462841314243727826941569954125384522233795629521155389745713798246071907492365062512521474965012924607857440577856404307124237116387085337087671914959900909379028727767057 n = p*q phi = (p-1) * (q-1) d = gmpy2.invert(e,phi) # d ≡ e-1 % n 逆元 m = pow(c,d,n) # m ≡ cd % n print(long_to_bytes(m))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?