本文目录
- 文章前言
- 一、RSA的诞生
- 1、加密算法的前世今生
- ① 《六韬·龙韬》中的阴符与阴书
- ② 古罗马:凯撒密码
- ③ 斯巴达:塞塔式密码(Scytale)
- 2、对称加密的脆弱性
- 3、非对称加密算法的出现
- 二、RSA中的数学概念与定理
- 1、质数理论
- 2、关于互质
- 3、欧拉函数
- ① 基本概念
- ② 计算过程
- 4、欧几里得算法
- ① 算法原理
- ② 算法步骤
- ③ 算法示例
- 5、扩展欧几里得算法
- ① 工作原理
- ② 算法步骤
- ③ 算法示例
- 6、模运算
- ① 模运算的性质
- ② 应用实例
- 7、同余方程
- ① 解同余方程的方法
- A、 当 a 和 m 互质
- B、 当 a 和 m 不互质
- ② 同余方程组
- ③ 算法示例
- 三、RSA加密与解密全过程
- 1、关于密钥生成
- 2、加密样例
- ① 密钥生成
- ② 加密
- ③ 解密
- 四、使用python完成全过程
- 1、全都自己来
- 2、调用成熟的库
- 3、pem文件
文章前言
为了彻彻底底的研究明白RSA算法,真的是。。煞(lei)费(si)苦(wo)心(le),首先我希望自己前前后后、彻彻底底的琢磨明白,其次我也希望我把他分享出来以后,大家都能读得懂,这篇文章真的是我迄今为止写的又臭又长的一篇文章了,但是我能保证的是,你读完了以后,绝对不会再问RSA是啥玩意儿了。。
一、RSA的诞生
1、加密算法的前世今生
人们在交流信息的时候,如果不想被别人听到,总会低声细语,通过降低音量的方式使得第三方听不到在交流的内容是什么,但并不是所有场景下都支持低声细语的,只有你和一个人离得近,坐在邻座,或者站在旁边,才可以窃窃私语。
想象一下,如果你的朋友在夏威夷的海滩上晒着太阳,而你在青藏高原拿着食物喂着野狼,这个时候你突然发现了一个外星飞碟,你想立刻告诉你的朋友,又不想被其他任何人知道,你想和他说悄悄话显然是不可能的了,除非你的朋友有顺风耳。
这个时候,你就需要使用一种加密的方式,把你的消息加密以后,再传递出去,万一路上不小心被他人拦下来了,也看不懂你写的是个啥东西,经过你的苦思冥想,你决定用飞鸽传书告诉你朋友,你发现了一个惊天大秘密。
准备好了一个可以送信过去的鸽子以后(假设这只鸽子是一个放出去能准确找到你朋友的鸽子),你开始奋笔疾书,写下了:
这个时候,你反应过来,我还没加密呢,如果被别人拦截下来,不就被外人知道了吗?然后,经过你的苦思冥想后,决定按以下方式发信。
加密方式: 文字信息2和4对调,1和5对调
解密方式: 文字信息2和4对调,1和5对调
加密后发信:
为了你的朋友看到消息以后能够看得懂,你还需要把加密规则也飞鸽传书过去:
一个不小心,你发现了一个东西,它叫做:对称加密
所谓的 对称加密
就是:
- 你在发信之前,选择了一种对信息进行
加密(Encrypt)
的方式。 - 你朋友收到信息后,要利用相同的方式对信息进行
解密(Decrypt)
。
这种加密方式弊端就是,你发送出去的解密方式,如果被人抓到了,那么你的信息加密就失去意义了。
加密思想的起源可以追溯到古代,而且不同文化和时期可能存在独立发展的加密方法。
① 《六韬·龙韬》中的阴符与阴书
《六韬》是中国古代著名的兵书之一,相传是姜子牙所作,但实际上可能是战国至汉代的作品。《六韬》分为文韬、武韬、龙韬、虎韬、豹韬和犬韬六个部分,每个部分又分为若干篇。
《六韬·龙韬·阴符第二十四》 主要讲述了古代军事通信中使用的秘密通信方式——“阴符”。
以下是该篇的原文和白话译文:
原文:
武王问太公曰:“引兵深入诸侯之地,三军卒有缓急,或利或害。吾将以近通远,从中应外,以给三军之用,为之奈何?”
太公曰:“主与将有阴符凡八等:凡大胜克敌之符,长一尺;破军杀将之符,长九寸;野战之符,长八寸;攻城之符,长七寸;入与诸侯战之符,长六寸;前哨后哨之符,长五寸;警备之符,长四寸;已败之符,长三寸。主将各持其符,凡八等。主发将符,有小变,则以左手奉之;有大变,则以右手奉之。”
白话译文:
武王问太公说:“率领军队深入到敌国境内,三军突然遇到紧急情况,有的有利,有的有害。我想要从近处沟通远处,使内部响应外部,以供应三军的需要,应当怎么办呢?”
太公回答说:“君主与将领之间有八种阴符作为秘密通信的手段:凡是代表大胜并战胜敌人的符信,长度为一尺;破敌军并杀死敌将的符信,长度为九寸;用于野战的符信,长度为八寸;用于攻城的符信,长度为七寸;用于与诸侯作战的符信,长度为六寸;前哨和后哨的符信,长度为五寸;用于警备的符信,长度为四寸;表示已经失败的符信,长度为三寸。君主和将领各自持有这八种不同的符信。当君主发出将符时,如果只是小的变化,就用左手托着它;如果是大的变化,就用右手托着它。”
由此我们可知,阴符通信的加密规则为:
Column 1 | Column 2 |
---|---|
大胜并战胜敌人的符信 | 长度为一尺 |
破敌军并杀死敌将的符信 | 长度为九寸 |
用于野战的符信 | 长度为八寸 |
用于攻城的符信 | 长度为七寸 |
用于与诸侯作战的符信 | 长度为六寸 |
前哨和后哨的符信 | 长度为五寸 |
用于警备的符信 | 长度为四寸 |
表示已经失败的符信 | 长度为三寸 |
《六韬·龙韬·阴书第二十五》 主要讲述了古代军事通信中使用的秘密通信方式——“阴书”。
以下是该篇的原文和白话译文:
原文:
武王问太公曰:“引兵深入诸侯之地,主将欲合兵,行无穷之变,图不测之利。其事繁多,符不能明;相去辽远,言语不通。为之奈何?”太公曰:“诸有阴事大虑,当用书,不用符。主以书遗将,将以书问主。书皆一合而再离,三发而一知。再离者,分书为三部。三发而一知者,言三人,人操一分,相参而不知情也。此谓阴书。敌虽圣智,莫之能识。”武王曰:“善哉。”
白话译文:
武王问太公说:“率领军队深入敌国的领土,主将想要集结兵力,进行变化无穷的战术,谋取不可预测的利益。但事情繁多,用符令难以明确传达;而且距离遥远,语言不通。这该怎么办呢?”太公回答说:“所有秘密的大事和深思熟虑的计划,应当用书写的方式,而不是用符令。君主通过书信来传达给将领,将领通过书信来询问君主。书写的方式是将书信分成三部分,然后分别发送,只有三部分合在一起才能明白其意。将书信分为三部分,意味着分成三份,每份由不同的人携带,他们相互之间不知道其他部分的内容。这就是所谓的阴书。即使敌人再聪明,也无法识别。”
② 古罗马:凯撒密码
凯撒密码被认为是最早的加密算法之一,它由古罗马统治者尤利乌斯·凯撒使用,大约在公元前1世纪。这是一种简单的替换加密,通过将字母表中的字母向后移动固定的位数来加密消息。例如,如果偏移量是3,那么字母“A”会被替换为“D”,依此类推。这种方法在当时是一种有效的保密手段,但在现代由于其简单性而很容易被破解。
③ 斯巴达:塞塔式密码(Scytale)
公元前5世纪左右,斯巴达人发明了一种被称为塞塔式密码的加密技术。这种技术涉及将一条皮带缠绕在一个木棍上,然后沿着木棍写下信息。当皮带解开后,上面的文字看起来是混乱无序的。只有当接收方使用相同直径的木棍重新缠绕皮带时,信息才能被正确解读。
这些早期的加密方法主要源于军事和政治通信的需要,目的是为了防止敌人截获并解读敏感信息。在古代,信息的安全性对于战争策略的制定和执行至关重要,因此催生了这些加密技术的发展。随着时间的推移,加密技术不断进化,从简单的替换和转换方法发展到基于复杂数学原理的现代加密算法。
2、对称加密的脆弱性
(1)共享密钥的难题:你和一个朋友想要通过写信来保密对话,你们需要一个只有你们俩知道的秘密密码。问题是,你们得先找到一个安全的方式告诉对方这个密码。如果有人偷听到了你们交换密码的过程,那这个密码就不再保密了。
(2)密码太简单:如果你们的秘密密码太简单,比如只用几个字母,别人很快就能猜出来。
(3)生成密码的方法不好:如果你们用一个容易预测的方法来生成密码,比如总是用你生日的月份和日期,那别人可能很快就能猜到。
(4) 密码重复使用:如果你们总是用同一个密码,别人只要知道一次,就能破解你们所有的通信。
(5)被人偷看:如果你们写信的时候,有人偷偷从旁边看,他们可能就能从你们写信的方式中猜出密码。
(6)管理很多密码很麻烦:如果你们有很多笔友,每个人都需要一个独特的密码,那么记住和保管这么多密码就会变得非常困难。
(7)不能确认身份:对称加密不能告诉你信是不是你朋友写的,因为任何人都可以用同一个密码来写信。
(8)信任问题:如果你们中的一个人不可靠,告诉了别人密码,那你们的秘密就保不住了。
3、非对称加密算法的出现
非对称加密算法的概念产生于20世纪70年代,它标志着密码学领域的一个重大突破。在此之前,加密通信主要依赖于对称加密算法,这意味着发送方和接收方必须共享相同的密钥以进行加密和解密操作。
这种模式下,密钥的分发和管理变得非常复杂且不安全,尤其是在涉及大量用户或无法安全直接传递密钥的情况下。
非对称加密算法的理论基础可以追溯到1976年,当时由惠特菲尔德·迪菲(Whitfield Diffie)和马丁·赫尔曼(Martin Hellman)提出的“Diffie-Hellman密钥交换协议”。
他们的论文《New Directions in Cryptography》首次公开讨论了非对称加密的可能性,即使用一对数学相关的密钥:一个公钥和一个私钥。公钥可以公开地分发给任何人,而私钥则由个人秘密保存。
点击查看论文《New Directions in Cryptography》下载地址
同年,詹姆斯·埃利斯(James H. Ellis)、克莱夫·卡弗(Clifford Cocks)和马尔科姆·威廉森(Malcolm J. Williamson)在英国政府通信总部(GCHQ)独立地开发了类似的概念,但这些成果直到多年后才被公开。
1977年,罗纳德·李维斯特(Ron R
ivest)、阿迪·萨莫尔(Adi S
hamir)和伦纳德·阿德曼(Leonard A
dleman)发明了RSA算法,这是第一个广泛使用的非对称加密算法,算法的名字取自于这三个人姓氏的第一个字母
。
RSA算法的发明极大地推动了非对称加密的应用,因为它不仅提供了加密和解密的功能,还支持数字签名,这在身份验证和确保数据完整性方面非常重要。
二、RSA中的数学概念与定理
1、质数理论
质数(也被称为素数)是数学中的一个基本概念,通俗地说,质数就是那些只能被1和它自己整除的数字。
例如,5是一个质数,因为它只能被1和5整除,没有任何其他数字能够整除它。另一方面,像6这样的数字就不是一个质数,因为除了1和6之外,它还可以被2和3整除。
更具体一点,质数的定义如下:
- 质数是大于1的自然数,除了1和它本身以外,不能被任何其他自然数整除。
- 例如,2、3、5、7、11、13等都是质数。
- 相反,4、6、8、9、10等就不是质数,它们被称为合数,因为它们可以被除了1和它自己以外的其他数整除。
- 1不是质数,也不是合数,它是一个特殊的数字,在质数和合数的分类中不占一席之地。
- 最小的质数是2,它是唯一的偶数质数,所有的其他质数都是奇数。
2、关于互质
互质(有时也称为互素)指的是两个或多个整数之间的特定关系。如果两个或多个整数除了1以外没有其他的公约数,那么这些整数就称为互质。
例如:
- 数字3和5互质,因为它们没有除了1以外的共同因子。
- 数字8和9互质,尽管它们都不是质数,但它们没有共同的质因数。
互质的性质还包括:
- 任意两个不同的质数总是互质的。
- 一个质数和一个不是其倍数的合数是互质的。
- 任何相邻的两个整数是互质的,因为它们之间不可能有大于1的公共因子。
3、欧拉函数
① 基本概念
欧拉函数,记作φ(n)
,以瑞士数学家莱昂哈德·欧拉
的名字命名。
欧拉函数φ(n)
就是用来告诉你,在所有小于n的正整数中,有多少个数字与n互质(即它们之间没有公共的除数,除了1以外)。
例如,n=9,那么小于9的正整数有1, 2, 3, 4, 5, 6, 7, 和8。其中,只有3, 6与9有共同的除数(除了1以外)。因此,与9互质的数字有1, 2, 4, 5, 7和8。所以,欧拉函数φ(9)等于6,因为有6个数字与9互质。
② 计算过程
欧拉函数φ(n)
给出的是小于或等于 n 的正整数中与 n 互质的数的数量。
对于一个数 n,如果它能被分解成质因数
的幂的乘积形式:
n = p 1 k 1 p 2 k 2 p 3 k 3 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ p r k r n = p1^{k1}p2^{k2}p3^{k3}········pr^{kr} n=p1k1p2k2p3k3⋅⋅⋅⋅⋅⋅⋅⋅prkr
那么φ(n)
可以通过下面的公式计算:
ψ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ( 1 − 1 p 3 ) ⋅ ⋅ ⋅ ⋅ ⋅ ( 1 − 1 p r ) \psi(n) = n(1-\frac 1 {p1})(1-\frac 1 {p2})(1-\frac 1 {p3})·····(1-\frac 1 {pr}) ψ(n)=n(1−p11)(1−p21)(1−p31)⋅⋅⋅⋅⋅(1−pr1)
其中p1、p2、p3····pr 是数字 n 的不同质因数,而 k1、k2、k3·····kr别是这些质因数的指数。
【计算实例一】:
1、随便选择一个质数,比如12
2、对12
进行质因数分解
3、
n = p 1 k 1 p 2 k 2 p 3 k 3 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ p r k r n = p1^{k1}p2^{k2}p3^{k3}········pr^{kr} n=p1k1p2k2p3k3⋅⋅⋅⋅⋅⋅⋅⋅prkr
12 = 2 2 × 3 1 12 = 2^2 × 3^1 12=22×31
4、对12
进行质因数分解
后,我们可以知道:p1 = 2, p2 = 3。
5、解答:为什么 12 没有分解为 2 × 2 × 3,因为合并同类项后,2 x 2 就是 2的平方。
6、解答:为什么 12 没有分解成为 3 x 4,因为 4 虽然是 12 的因数,但不是质数,质因数要同时满足质数和因数。
7、带入欧拉函数公式
ψ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ( 1 − 1 p 3 ) ⋅ ⋅ ⋅ ⋅ ⋅ ( 1 − 1 p r ) \psi(n) = n(1-\frac 1 {p1})(1-\frac 1 {p2})(1-\frac 1 {p3})·····(1-\frac 1 {pr}) ψ(n)=n(1−p11)(1−p21)(1−p31)⋅⋅⋅⋅⋅(1−pr1)
ψ ( 12 ) = 12 ( 1 − 1 2 ) ( 1 − 1 3 ) \psi(12) = 12(1-\frac 1 {2})(1-\frac 1 {3}) ψ(12)=12(1−21)(1−31)
ψ ( 12 ) = 12 × 1 2 × 2 3 \psi(12) = 12 × \frac 1 {2} × \frac 2 {3} ψ(12)=12×21×32
ψ ( 12 ) = 4 \psi(12) = 4 ψ(12)=4
8、得到结论,12 的 互质数有4个。
【计算实例二】:
1、随便选择一个质数,比如13
2、对13
进行质因数分解
3、
n = p 1 k 1 p 2 k 2 p 3 k 3 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ p r k r n = p1^{k1}p2^{k2}p3^{k3}········pr^{kr} n=p1k1p2k2p3k3⋅⋅⋅⋅⋅⋅⋅⋅prkr
13 = 1 3 1 13 = 13^1 13=131
4、对13
进行质因数分解
后,我们可以知道:p1 = 13。
7、带入欧拉函数公式
ψ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ( 1 − 1 p 3 ) ⋅ ⋅ ⋅ ⋅ ⋅ ( 1 − 1 p r ) \psi(n) = n(1-\frac 1 {p1})(1-\frac 1 {p2})(1-\frac 1 {p3})·····(1-\frac 1 {pr}) ψ(n)=n(1−p11)(1−p21)(1−p31)⋅⋅⋅⋅⋅(1−pr1)
ψ ( 13 ) = 13 ( 1 − 1 13 ) \psi(13) = 13(1-\frac 1 {13}) ψ(13)=13(1−131)
ψ ( 13 ) = 13 × 12 13 \psi(13) = 13 × \frac {12} {13} ψ(13)=13×1312
ψ ( 12 ) = 13 \psi(12) = 13 ψ(12)=13
8、得到结论,13 的 互质数有12个。
【计算实例三】:
1、随便选择一个质数,比如24
2、对24
进行质因数分解
3、
n = p 1 k 1 p 2 k 2 p 3 k 3 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ p r k r n = p1^{k1}p2^{k2}p3^{k3}········pr^{kr} n=p1k1p2k2p3k3⋅⋅⋅⋅⋅⋅⋅⋅prkr
24 = 2 3 × 3 1 24 = 2^3 × 3^1 24=23×31
4、对24
进行质因数分解
后,我们可以知道:p1 = 2,p2 = 3。
7、带入欧拉函数公式
ψ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ( 1 − 1 p 3 ) ⋅ ⋅ ⋅ ⋅ ⋅ ( 1 − 1 p r ) \psi(n) = n(1-\frac 1 {p1})(1-\frac 1 {p2})(1-\frac 1 {p3})·····(1-\frac 1 {pr}) ψ(n)=n(1−p11)(1−p21)(1−p31)⋅⋅⋅⋅⋅(1−pr1)
ψ ( 24 ) = 24 × ( 1 − 1 2 ) × ( 1 − 1 3 ) \psi(24) = 24×(1-\frac 1 {2})×(1-\frac 1 {3}) ψ(24)=24×(1−21)×(1−31)
ψ ( 24 ) = 24 × 2 6 \psi(24) = 24 × \frac {2} {6} ψ(24)=24×62
ψ ( 24 ) = 8 \psi(24) = 8 ψ(24)=8
8、得到结论,24 的 互质数8个。
4、欧几里得算法
欧几里得算法(Euclidean algorithm)是一种高效求解两个正整数最大公约数(Greatest Common Divisor, GCD)的方法。
① 算法原理
对于任意两个非负整数 a 和 b,其中 a ≥ b,假设 b > 0,则存在唯一的整数商 q 和余数 r(0 ≤ r < b)),满足下面的关系:
a = b × q + r
如果 d 是 a 和 b 的一个公约数,那么 d 必然也是 r 的公约数,这是因为 r = a - bq,所以任何能同时整除 a 和 b 的数也能整除 r。
同理,如果 d 是 b 和 r 的公约数,那么它也是 a 的公约数,因为 a = bq + r,所以任何能同时整除 b 和 r 的数也能整除 a。
因此,a 和 b 的最大公约数等于 b 和 r 的最大公约数。
② 算法步骤
- 初始化:给出两个非负整数 a 和 b,其中 a ≥ b。
- 除法:如果 b > 0,计算 a 除以 b 的余数 r。
- 递归:用 b 替换 a,用 r 替换 b,然后重复步骤2。
- 终止条件:当
b = 0
时,算法终止,此时的 a 即为原来 a 和 b 的最大公约数。
③ 算法示例
假设我们要找 48 和 18 的最大公约数:
- 48 = 18 × 2 + 12,所以新的 a = 18,b = 12。
- 18 = 12 × 1 + 6,所以新的 a = 12,b = 6。
- 12 = 6 × 2 + 0,此时 b = 0,算法停止,a = 6 即为最大公约数。
5、扩展欧几里得算法
扩展欧几里得算法的目标是找到两个整数 a 和 b 的最大公约数,并找到一组整数 x 和 y 使得等式 ax + by = a和b的最大公约数
成立。
① 工作原理
在每一步中,使用基本的欧几里得算法进行除法运算,但是除了跟踪商和余数之外,还跟踪一系列的系数,这些系数最终会帮助我们找到所需的 x 和 y。
② 算法步骤
- 初始化:设 a 和 b 为任意两个整数,初始设定 x1 = 0, x2 = 1, y1 = 1, y2 = 0。
- 循环:只要 b 不为零,重复以下步骤:
- 执行除法:a = bq + r,其中 q 是商,r 是余数。
- 更新 a 和 b:a 变成 b,b 变成 r。
- 更新系数:计算新的 x 和 y,使用公式 x = x2 - q x1 和 y = y2 - q y1,然后更新 x1, x2, y1, y2。
- 结束:当 b 变为零时,此时的 a 就是 最大公约数,而最后的 x2 和 y2 就是满足 ax + by = a和b的最大公约数 的 x 和 y。
③ 算法示例
假设我们要找到 30 和 12 的最大公约数,并找出使得 30x + 12y = 6 的 x 和 y。
- 初始化:a = 30, b = 12, x1 = 0, x2 = 1, y1 = 1, y2 = 0。
- 循环开始:
- 30 = 12 × 2 + 6,所以 q = 2,r = 6。
- 更新:a 变为 12,b 变为 6。
- 更新系数:x = 1 - 2 × 0 = 1,y = 0 - 2 × 1 = -2,所以 x1 = 1,x2 = 0,y1 = -2,y2 = 1。
- 继续循环:
- 12 = 6 × 2 + 0,所以 q = 2,r = 0。
- 更新:a 变为 6,b 变为 0。
- 更新系数:x = 0 - 2 × 1 = -2,y = 1 - 2 × -2 = 5。
- 结束:此时 b = 0,所以 30 和 12 的最大公约是就是 6,且 -2 × 30 + 5 × 12 = 6。
这样,我们就得到了 30 和 12 的最大公约数是 6,并且找到了一组 x = -2 和 y = 5,使得 30x + 12y = 6。
6、模运算
模运算是数论中的一个基本概念,它在计算机科学、密码学、抽象代数以及许多其他领域都有广泛应用。模运算的基本思想是关注整数除法的余数部分。当我们说“a 模 n”时,我们指的是 a 除以 n 的余数。通常写成 a mod n 或者在同余关系
中写作 a ≡ b (mod n ),表示 a 和 b 在模 n 下同余,即 a 和 b 的差是 n 的倍数。
① 模运算的性质
模运算有一些重要的性质,包括但不限于:
-
加法:如果 a ≡ b ( mod n ) , c ≡ d ( mod n),那么 a + c ≡ b + d ( mod n)。
-
乘法:如果 a ≡ b ( mod n ) , c ≡ d ( mod n ),那么 ac ≡ bd ( mod n )。
-
幂:如果 a ≡ b ( mod n ),那么对于任何正整数 k来说,存在 a k ≡ b k ( m o d n ) a^k ≡ b^k (mod{n}) ak≡bk(modn)
-
逆元:如果 (a) 和 (n) 互质(即它们的最大公约数是 1),那么存在一个整数 b,使得 ab ≡ 1 ( mod n )。这种情况下,b 称为 a 的模逆元。
-
循环性:模运算的结果总是在 0 到 (n-1) 的范围内。
② 应用实例
假设我们有模 5 的运算,考虑一些基本的模运算实例:
- 7 mod 5 = 2,因为 7 = 5 × 1 + 2。
- (-3) mod 5 = 2,因为 (-3) = 5 × (-1) + 2。
即使原始数字是负数,模运算的结果也是非负的。
7、同余方程
同余方程是基于模运算的概念构建的方程,一般形式可以表示为:
ax ≡ b ( mod m )
其中,a, b, 和 m 都是已知的整数,(m > 0) 是模数,而 x 是未知数。这个方程表示的是,存在某个整数 x,使得 ax 和 b 在模 m 下同余。换句话说,(ax - b) 能被 m 整除。
① 解同余方程的方法
解同余方程的方法依赖于 a 和 m 的最大公因数。以下是几种情况:
A、 当 a 和 m 互质
如果 a 和 m 的最大公因数为 1,即 a 和 m 互质,那么同余方程有唯一解模 m。在这种情况下,可以使用扩展欧几里得算法找到 a 的模逆元 a 1 a^1 a1 模 (m),然后通过下面的步骤求解 (x):
a x ≡ b ( m o d m ) ax ≡ b ( mod {m}) ax≡b(modm)
a − 1 a^{-1} a−1 × ax ≡ a − 1 a^{-1} a−1 × b ( mod m)
x ≡ a − 1 a^{-1} a−1 × b ( mod m)
B、 当 a 和 m 不互质
如果 a 和 m 不互质,即它们的最大公因数 (d > 1),那么解的存在性和个数取决于 b 是否能被 d 整除。如果 b 能被 d 整除,则方程有解,并且解的个数等于 d。
② 同余方程组
有时,我们需要同时解决多个同余方程,被称为同余方程组。
中国剩余定理(Chinese Remainder Theorem)提供了解决多个同余方程组的一种方法。
中国剩余定理保证了如果有 (n) 个方程,每个方程的形式为:
x ≡ a i ( m o d m i ) x \equiv a_i \pmod{m_i} x≡ai(modmi)
并且所有的 m i {m_i} mi 都是两两互质的,则存在一个唯一的解 (x) 模 ∏ m i \prod m_i ∏mi。
③ 算法示例
让我们看一个简单的同余方程的例子:
3 x ≡ 4 ( m o d 7 ) 3x \equiv 4 \pmod{7} 3x≡4(mod7)
首先,我们找到 3 的模逆元模 7。由于 3 和 7 互质,这样的逆元是存在的。
然后,我们可以用这个逆元来求解 x。
让我们计算 3 的模逆元模 7,并求解这个同余方程。
同余方程 3 x ≡ 4 ( m o d 7 ) 3x \equiv 4 \pmod{7} 3x≡4(mod7) 的解是 x ≡ 6 ( m o d 7 ) x \equiv 6 \pmod{7} x≡6(mod7)。这意味着在模 7 下,x 的值可以是任何形如 6 + 7 k 6 + 7k 6+7k 的数,其中 k k k 是任意整数。
三、RSA加密与解密全过程
在大体上把所有的铺垫全都说完了以后,我们来看一下RSA加密的全过程。
1、关于密钥生成
(1)选择两个大质数 p = 和 q 。
- 这些质数应该足够大,以确保安全性,不然越短越容易被破解呀。
(2)计算模数 n :
- n = p × q
(3)计算欧拉函数 ϕ ( n ) \phi(n) ϕ(n):
- ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n) = (p-1)(q-1) ϕ(n)=(p−1)(q−1)
- 这是小于 ( n ) 的正整数中与 ( n ) 互质的数的数量。
(4)选择公钥指数 e:
- e 必须小于 ϕ ( n ) \phi(n) ϕ(n) 并且与 ϕ ( n ) \phi(n) ϕ(n)互质。
- 常见的选择是 e = 65537 ( 2 16 + 1 ) e = 65537 ( 2^{16} + 1) e=65537(216+1)。
(5)确定私钥指数 ( d ):
- 找到 ( d ),使得 d e ≡ 1 ( mod ϕ ( n ) ) de \equiv 1 \ (\text{mod } \phi(n)) de≡1 (mod ϕ(n))。
- d 是 e 在模 ϕ ( n ) \phi(n) ϕ(n) 意义下的乘法逆元。
然后我们最终确认了:公钥是(e, n),而私钥是(d, n)。
2、加密样例
① 密钥生成
- 选择两个大素数 ( p ) 和 ( q ),例如 ( p = 61 ) 和 ( q = 53 )。
- 计算 ( n ), n = p × q n = p \times q n=p×q。在这个例子中, n = 61 × 53 = 3233 n = 61 \times 53 = 3233 n=61×53=3233。
- 计算欧拉函数 ϕ ( n ) \phi(n) ϕ(n), ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n) = (p-1)(q-1) ϕ(n)=(p−1)(q−1)。因此, ϕ ( 3233 ) = ( 61 − 1 ) ( 53 − 1 ) = 3120 \phi(3233) = (61-1)(53-1) = 3120 ϕ(3233)=(61−1)(53−1)=3120。
- 选择一个整数 ( e ) 作为公钥的一部分,它需要满足 1 < e < ϕ ( n ) 1 < e < \phi(n) 1<e<ϕ(n),并且 e 和 ϕ ( n ) \phi(n) ϕ(n) 互质。通常 e = 65537 e = 65537 e=65537,但在本例中为了简单,我们选择 e = 17 e = 17 e=17。
- 找到 ( d ),使得 d × e ≡ 1 m o d ϕ ( n ) d \times e \equiv 1 \mod{\phi(n)} d×e≡1modϕ(n),即找到 d 满足 d e − k ϕ ( n ) = 1 de - k\phi(n) = 1 de−kϕ(n)=1 的最小正整数解,其中 k 也是一个整数。
假设 d = 2753。
现在,公钥是 ( n , e ) = ( 3233 , 17 ) (n, e) = (3233, 17) (n,e)=(3233,17),私钥是 ( n , d ) = ( 3233 , 2753 ) (n, d) = (3233, 2753) (n,d)=(3233,2753)。
② 加密
假设我们要加密的字母是“A”,其ASCII值为65。使用公钥 ( n , e ) (n, e) (n,e) 进行加密:
C = M e m o d n C = M^e \mod{n} C=Memodn
C = 6 5 17 m o d 3233 C = 65^{17} \mod{3233} C=6517mod3233
计算得到加密后的密文:2790。
③ 解密
使用私钥 ( (n, d) ) 进行解密:
M = C d m o d n M = C^d \mod{n} M=Cdmodn
M = C 2753 m o d 3233 M = C^{2753} \mod{3233} M=C2753mod3233
计算得到解密后的明文 ( M ),应该等于原来的ASCII值65,代表字母“A”。
四、使用python完成全过程
1、全都自己来
# 导入随机数模块
import random# 定义一个函数检查一个数是否为素数
def is_prime(num, k=5): # 使用Miller-Rabin测试素性# 如果num等于2或者3,则它是素数if num == 2 or num == 3:return True# 如果num小于等于1或能被2整除,则它不是素数if num <= 1 or num % 2 == 0:return False# 初始化s为0,r为num-1s = 0r = num - 1# 循环直到r为奇数while r % 2 == 0:s += 1r //= 2# 进行k次随机测试for _ in range(k):# 选择一个在2到num-1之间的随机数aa = random.randrange(2, num - 1)# 计算a^r mod numx = pow(a, r, num)# 如果x不等于1且不等于num-1,继续测试if x != 1 and x != num - 1:j = 1# 当j小于s且x不等于num-1时,计算x^2 mod numwhile j < s and x != num - 1:x = pow(x, 2, num)# 如果x等于1,则num不是素数if x == 1:return Falsej += 1# 如果循环结束x仍不等于num-1,则num不是素数if x != num - 1:return False# 如果所有测试通过,则num是素数return True# 生成一个特定位数长度的素数
def generate_prime(bit_length):# 循环直到找到一个素数while True:# 生成一个候选素数candidate = random.getrandbits(bit_length)# 如果候选数是素数,返回它if is_prime(candidate):return candidate# 扩展欧几里得算法求模逆元
def extended_gcd(a, b):# 如果a为0,返回b作为最大公因数,以及y和xif a == 0:return b, 0, 1# 递归调用扩展欧几里得算法else:g, y, x = extended_gcd(b % a, a)return g, x - (b // a) * y, y# 求a关于模m的逆元
def mod_inverse(a, m):# 调用扩展欧几里得算法得到最大公因数g和系数x, yg, x, _ = extended_gcd(a, m)# 如果g不等于1,则不存在逆元if g != 1:raise Exception('模逆元不存在')else:# 返回逆元return x % m# 生成RSA密钥对
def generate_rsa_keys(bit_length=1024):# 生成两个大素数p和qp = generate_prime(bit_length // 2)q = generate_prime(bit_length // 2)# 计算n=p*qn = p * q# 计算欧拉函数phi(n)=(p-1)*(q-1)phi = (p - 1) * (q - 1)# 公钥指数e通常取65537e = 65537# 计算d作为e关于phi(n)的逆元d = mod_inverse(e, phi)# 返回公钥和私钥return ((n, e), (n, d))# RSA加密
def rsa_encrypt(message, public_key):# 解析公钥n和en, e = public_key# 对消息中的每个字符进行加密,然后存储为密文列表encrypted = [pow(ord(char), e, n) for char in message]# 返回密文列表return encrypted# RSA解密
def rsa_decrypt(ciphertext, private_key):# 解析私钥n和dn, d = private_key# 对密文中的每个字符进行解密,然后转换为明文字符decrypted = [chr(pow(char, d, n)) for char in ciphertext]# 将解密后的字符列表组合成字符串并返回return ''.join(decrypted)# 生成RSA密钥对
public_key, private_key = generate_rsa_keys()
# 打印公钥
print("公钥:", public_key)
# 打印私钥
print("私钥:", private_key)# 设置要加密的消息
message = "RSA Encrypt"
# 加密消息
encrypted_message = rsa_encrypt(message, public_key)
# 打印加密后的消息
print("加密后的消息:", encrypted_message)# 解密消息
decrypted_message = rsa_decrypt(encrypted_message, private_key)
# 打印解密后的消息
print("解密后的消息:", decrypted_message)
runcell(0, '/Users/banana/RSA_encrypt.py')
公钥: (59646419748693400781167231918864578733595881174804771090691972253685313763625106089222827068769797944860366103271114236655040756661680702413471164294230693450803962565277497837441778522621168571626120159873073803785793147444167847354028305167787745213194659180048687599897196136705477534595049586678151439393, 65537)
私钥: (59646419748693400781167231918864578733595881174804771090691972253685313763625106089222827068769797944860366103271114236655040756661680702413471164294230693450803962565277497837441778522621168571626120159873073803785793147444167847354028305167787745213194659180048687599897196136705477534595049586678151439393, 32142646234411169803141479312122012407257056860255734937668315365112901508917830717809521088663548133399965663201335756838825005768048237898233991628840881545199125028405522177220740077175847703801994326668304462927740266904953009915474535302298170624022031245633159914122246898395815497024072948378077296981)
加密后的消息: [58006013052348661870102523917402564232350749859541740117196952175166479850188431091616403478464099956431597504683788152777093292684514702027148448757443408647534347289687245885095578173291239739818268200472965722801727451926570113893155714578285866449783415055901109930087882617218534404091332012846494227113, 41410225121882370000814813603209301497750342692236402964115743835111101514306382951295925164763718017873378386749064561649667778637082648033281619454404021810260835111725841392954616074108168802933404617150509368028787255988132241664761583629715006688390870210511261512959377102174820525926934433999702206084, 32052877136811107868725941737162146294374662165598944205879981474671086803382274430410735763688525739294139085956217572028800165815727620758252980861645944806501322670560133083601447115326766052924047435365918894505961356051303257555018475648261928814670142855025808200087253271970754065063692422210157812523, 16594717360316096183435571781889547842462955463204348086990330933442989296521998827790205258767313758698026846281774354674499639061430393985249719137336390901574872932674264271927674941992846701216058543510723220164324633569624165639716459062991574090225833195189230155912641233159512489240055124217135815171, 37841305421340048873116873939630191490011771406238387441386085578594159563526335626669467558636306192680432146298190539885294619232487918368474474670669001486872782887600777955041432233291061058725105464514941639397061514778996660754592352588754282730421504080742649956915717455177212203787313458340776912145, 14644916005002088970555652721784759173891582941585142839523017190877128787359446593657518200754176264331614461071204059632625282270117885047683871910256132057600525127751300789399922272096168115072177227713714740498357142006948470790304622070692566176481026956814088698998959141648163695912450717049677539404, 49241012204145765727019110222265102226443695146620596584327809428450256469802615330847815556809557372253207931426554530738939032710655134858545591577091497287886805118579494493553816879151332181384055412231459029979683411279808344186353685274499484469080310262612372450343034097207841853480857812195568137536, 11867845880292860518188162423566056835623716220152805406523748466081737949238659191105150254764514793528601313400592056012426088450603656507093298392480954463522307462934511456684362436269016550596060704528494946390043854901272757075309041545157690939543058516567299797886927307102941673960524727788975137407, 2619611384603181268637368037721216065766762798123501537294280536043326703986230265740616895927905085267091097359457718699755836676641362465135732148170917765032763215078521357950008585939988220712133992780309289107565169076964354866141644828704537139459376637495220452747656245155431916070428281460570577709, 29998005705624795368273973405102713053354412225467723783502358968317349005630690894350494504588014015219036788745154987760091735750048584657280507827573601214796931300987794164806920700191173664137738633739677433004095588868824458439082089508943955793455836223594334207424694816856847119561501417101399214880, 56974449162471004859243206105697128458563585376379498265732598443131959100657725848710638606180453072288946755249116244878612540836231581976945614967507550016017449682247812762158649985150783702525894049842404535744020706265863821269075070508045908484302028463229514764998615480017497102783799258386390051250]
解密后的消息: RSA Encrypt
2、调用成熟的库
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes# 生成RSA密钥对
def generate_rsa_keys():private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend())public_key = private_key.public_key()return private_key, public_key# 保存密钥到文件
def save_key_to_file(key, filename, password=None):pem = key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.TraditionalOpenSSL,encryption_algorithm=serialization.BestAvailableEncryption(password) if password else serialization.NoEncryption()) if isinstance(key, rsa.RSAPrivateKey) else key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo)with open(filename, 'wb') as f:f.write(pem)# 从文件加载密钥
def load_key_from_file(filename, password=None):with open(filename, 'rb') as f:pem_data = f.read()if password:key = serialization.load_pem_private_key(pem_data,password=password,backend=default_backend())else:try:key = serialization.load_pem_private_key(pem_data,password=None,backend=default_backend())except ValueError:key = serialization.load_pem_public_key(pem_data,backend=default_backend())return key# RSA加密
def rsa_encrypt(message, public_key):ciphertext = public_key.encrypt(message,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None))return ciphertext# RSA解密
def rsa_decrypt(ciphertext, private_key):plaintext = private_key.decrypt(ciphertext,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None))return plaintext# 示例使用
if __name__ == "__main__":private_key, public_key = generate_rsa_keys()# 保存密钥save_key_to_file(private_key, 'private_key.pem')save_key_to_file(public_key, 'public_key.pem')# 加载密钥private_key = load_key_from_file('private_key.pem')public_key = load_key_from_file('public_key.pem')# 加密和解密message = b'Hello, World!'encrypted_message = rsa_encrypt(message, public_key)decrypted_message = rsa_decrypt(encrypted_message, private_key)print("原始消息:", message)print("加密后:", encrypted_message)print("解密后:", decrypted_message)
3、pem文件
PEM格式是一种用来存储证书、公钥、私钥和其他加密信息的文本格式。PEM格式的数据是以Base64编码的二进制数据。
在PEM格式中,数据被包裹在特定的开始和结束标记之间。
对于公钥、私钥或证书,常见的标记如下:
- 开始标记:
-----BEGIN <TYPE>-----
- 结束标记:
-----END <TYPE>-----
其中 <TYPE>
可以是 PRIVATE KEY
, PUBLIC KEY
, CERTIFICATE
, 或其他类型,具体取决于所包含的数据。
比如一个私钥文件是这样子滴:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwUx...(中间是一长串Base64编码的文本).../LlBQIDAQABAoIBAFY=
-----END RSA PRIVATE KEY-----
pem = key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo
)
这段代码是将key
对象(公钥)转换为PEM格式的字符串或字节串,其中包含了公钥的信息。
key = serialization.load_pem_private_key(pem_data,password=None,backend=default_backend()
)
这段代码的作用是从PEM格式的字符串或字节串中加载并解析出私钥数据,还原成可以在程序中操作的密钥对象。