RSA加密是比较常用的加密算法(并不会自己写到),本文尝试使用最基础的方式解释RSA中的每一步的计算步骤、理论依据及证明;即使是忘记了此前学过的所有密码学知识,跟随本文的思路也能完全理解RSA,不用死记硬背一些没有完全理解的东西。整个篇幅会有点长,如果遇到了某个已经理解了的公式或定理,可以直接跳过。
RSA加密流程
首先会介绍一下RSA的加密流程,其中有【】标注的内容会在后文做出解释:
-
找两个大质数:p、q;算出乘积n;可以保证任何人不能通过n,拆出p、q。
-
算出另一个数字fn = (p - 1) * (q - 1);这个fn的意思是:在1到n之间,与n互质的数的数量【1】(该注释解释为什么fn能表示这个个数),因为n没有办法被拆开,所以攻击者也计算不出fn。
-
然后选择一个质数e = 65537作为公钥(也可以选别的),e是质数。
-
找到私钥d,满足 e * d mod f == 1【2】(该注释介绍如何找到d)。
以上为准备工作;准备这些数据并保存私钥的我们称为接收方,通过明文获得公钥并使用使用公钥加密的称为发送方。
发送方获取到公钥[e, n]后,通过如下方式加密:
-
现假设有需要加密的数字m,保证m<n; (如果比较长,可以分段加,接收方分段解,始终保证m<n);
-
发送方计算出密文c = m ^ e mod n ,发送给接收方。
-
接收方获取到密文c和自己保存的私钥[d,n]后,通过如下方式解密:
-
接收方计算m' = c ^ d mod n。
-
m' == m 必成立,解密完成【3】(该注释解释为什么 m' == m成立,即RSA的理论基础)
【1】为什么fn = (p-1) * (q-1)表示[1, n]中与n互质的数的个数
- 因为p为质数,所以在[1, p]中,除p以外的数字,均与p互质,故fp = (p - 1)
- 因为q为质数,所以在[1, q]中,除q以外的数字,均与q互质,故fq = (q - 1)
- 因为n = p * q,所以n不是质数,故不能套用以上规则:但是可以通过排除法算出与n互质的数字的个数
- 在[1, n]中除n外共有数字 p * q - 1个;其中与n不互质的数字包括:A.所有比p小的数字,与q的乘积,共p-1个;B.所有比q小的数字与p的乘积,共q-1个;故有[1,n]中与n互质的数字个数:fn = p * q - 1 - (p - 1) - (q - 1) = p * q - p - q + 1 = (p - 1) * (q - 1)。
- 综上:在p、q为质数,n = p * q的情况下, fn = (p - 1) * (q - 1) 可以表示[1, n]之间与n互质的数字个数。
【2】在已知质数e、数字fn的情况下,如何找到d,使得e * d mod fn = 1
为了更清晰地阐明这个过程,这里先举一个简单的例子:
假设:p = 3;q = 13;则有n = 39;fn = 2 * 12 = 24;现在,我可以任意指定任意质数为公钥e,假设我指定e = 11,需要寻找一个数字d,满足 e * d mod fn = 1。
按照最直观的操作方式,暴力遍历d = 1、2、3、4...直到找到一个d,满足d * e mod fn = 1:
11 mod 24 = 11
22 mod 24 = 22
33 mod 24 = 9
44 mod 24 = 20
55 mod 24 = 7
66 mod 24 = 18
77 mod 24 = 5
88 mod 24 = 16
99 mod 24 = 3
110 mod 24 = 14
121 mod 24 = 1
结束;事实上,这个过程就是在寻找:
x * 11 - y * 24 = 1;x依次增加,y在总是保证结果为正的前提下尽量大,最终找到了x是11;
所以问题就变成了:我要得到1,至少需要用多少个11(公钥),这个"多少个"就是私钥。因为是私钥*公钥 mod n = 1。
然后,我们演示一下求解11和24最大公约数的过程,我们需要的并不是这个最大公约数,因为你要求的最大公约数一定是1,我们只是需要求解最大公约数过程中使用到的e的个数。
注意:事实上,因为RSA只规定了私钥p、q均为大质数,这里如果(p - 1) * (q - 1)恰好是e的倍数怎么办?在此种情况下,最大公约数是e而不是1,因此,如果遇到此种情况,需要重新换个e:你只需要计算一次(p - 1)*(q - 1)/e是否为整数即可:如果不为整数,则可以使用,如果为整数,重新挑选e,只要保证fn = (p - 1) * (q - 1)不为e的整数倍,那么,fn与e一定互质,在fn与e互质的情况下,最大公约数一定是1,这一点是在挑选e的时候需要保证的。
然后我们列出最大公约数求解过程(辗转相减):
24 - 11 = 13
13 - 11 = 2
11 - 2 = 9
9 - 2 = 7
7 - 2 = 5
5 - 2 = 3
3 - 2 = 1
这个过程有了,那么我们要的是什么值呢,要每一步使用到的11的个数,尾标注一下:
24 - 11 = 13 用到几个11? 1个 ,因为24不用11,11用1个11,所以一共是1个
13 - 11 = 2 用到几个11?2个 ,因为根据上一步:13用了1个11; 本步11用了1个11,所以一共2个
11 - 2 = 9 用到了几个11?3个,因为根据上一步:2用了2个11;本步11用了1个11,所以一共是3个
9 - 2 = 7 用到了几个11?5个,因为根据上一步:9用了3个11;上上步2用2个11,所以一共是5个
7 - 2 = 5 用到了几个11?7个,因为根据上一步:7用了5个;第二步2用了2个,一共7个
5 - 2 = 3 用到了几个11?9个,因为上一步:5用了7个;第二步2用了2个,一共9个
3 - 2 = 1 用到了几个11? 11个,因为上一步:3用了9个;第二步2用了2个;一共11个
结束,得1的时候,用到的11个11,前面的11就是d,后面的11就是e。(Emmm,这个例子不太好了,e和d相等了,但是我已经写到这了,就不重新找别的例子了,可以自行算其他的例子验证,e、d正常来说不都是一样的,这里恰好相等)
那为什么两种方法求出的使用11的个数是一致。如果你已经理解了,可以跳过下面的例子,如果还没理解可以读完这个例子:
【2.1】上下台阶例子
根据上边求解最大公约数的步骤,是不是很像是一个动态规划:列表记录每一步的结果,后一步的结果依赖前面的结果;提到动态规划,就不得不提上下台阶,如果我把题改成:一个小人上下台阶,每次只能从:A.向上走11个台阶 或 B.向下走24个台阶中选择一个方式走,起始位置是0,问:最少走多少次A才能走到1阶。
第一种方法:只要没超过24,就一直往上走11,超过24了,向下走24,直到走到1,记录向上走11的次数。
第二种方法,智能一点:
- 只要没超过24,我一直向上走11,超过了减24:上3次,下一次,到9;我记录一下:向上走9步需要3个11;那么下次当我快要抵达24的时候,是不是可以优先考虑9,直接用9步3次的结果。
- 从9开始,向上11到20,没超过;接着向上11,到31,超过了,此时,我可以看看,如果不用11,而是用最小的可以满足超过24的步数9,20 + 9 = 29,也超过了;那么我就直接用9;所以在原来3的基础上加1再加3得7;最终走到了5;记录:我向上走5阶,用7个11,下次可以用。
- 从5开始,向上11,到16,没超过;接着向上11,超过了,此时,我可以选一个小的值替代11:5行不行?不行,因为16 + 5 = 21 < 24; 9行不行?行,因为16 + 9 = 25 > 24;所以选9,那么也就是在原来7个11的基础上加1个11,再加3个11:7 + 1 +3 = 11,然后看到当前在25 - 24 = 1了,说明满足结果要求,故一共需要11个11能走到1阶。
第三种方法,再智能一点:
记录两个数,向上的最小阶数m,向下的最小阶数n,初始值m = 11,n = 24; 记录另外两个值stepM、stepN,表示走到向上/向下的当前最小阶数,用到11的个数,那么初始值:mNeed11 = 1、nNeed11 = 0。开始循环:
- 我先看看向上和向下哪个大?向下大:那么我可以用24减去尽可能多的11,得到一个向下的距离1最近的阶数:是不是24 - 2 * 11 = 2;然后用这个2替换之前的最小阶数24:m = 11, n = 2, 更新 nNeed11 = 2,因为减了两个11。
- 我再看看m、n哪个大?向上大:那么可以用11减去尽可能多的2,得到一个向上的距离1最近的阶数:是不是11 - 5 * 2 = 1;然后用这个1替换之前向上的最小阶数11:m = 1, n = 2, mNeed11 = mNeed11 + 5 * nNeed11 = 11,因为你用了一次向上m减去尽可能多的5个n,所以后面加了前一次的mNeed11。
此时m已经等于1了,所以退出循环。
然后看看这三个方法:方法一其实就是一个一个11向上加,超24减24的方法;方法三,其实就是辗转相除法求最大公约数,在求解e和fn的最大公约数过程中,记录使用到e的个数。
此例结束,总结:求解e * d mod fn = 1和辗转相除法求解最大公约数联系在一起时因为;前者需要的是后者中间产物,非最大公约数本身。
【3】m' == m 必成立
间隔比较远,重新描述一下问题:
m为明文,任意正整数;p、q为质数;n = p * q; fn = (p - 1) * (q - 1);
e为任取质数,d满足 e * d mod fn = 1;
c为密文,c = m ^ e mod n
m'为解密文,m' = c ^ d mod n
证明:m' == m
为了在讲述最终证明之前扫清一些障碍,首先证明三个小推论:
【3.1】模法分配率:"积的模 等于 模积模"
即:a * b mod c = (a mod c) * (b mod c) mod c ("模法分配率"我瞎编的,不知道怎么叫,这个读着顺口好记)
证明:将a、b拆成,关于c的倍数和余数之和的形式:
令: a = k * c + x; b = s * c + y;k、s为倍数,x、y为余数,x、y一定小于等于c。
那么有:a * b mod c = (k * c + x) * (s * c + y) mod c = (k * m* c^2 + k * c * y + m * c * x + x * y) mod c
前三项均包含c因子,故会被模掉,所以最终剩余:
a * b mod c = x * y mod c = (a mod c) * (b mod c) mod c
得证。
【3.2】若a与c互质 且 b与c互质,则a * b与c互质
反证法:若a * b与c不互质,则存在一个大于1的因子m,同时在a * b 和 c中;对于a * b,若想有因子m,则因子m不是在a中,就是在b中,或者是在a、b中;不论哪种情况,都与a、c互质和a、b互质矛盾,故不存在m;所以a * b 与 c互质。
得证。
【3.3】若a与b互质,则a mod b 与 b 互质
将a拆解成倍数和余数的形式 a = k * b + x;则x = a mod b;因为k * b一定与b不互质,如果x与b也不互质,那么k * b + x与b一定不互质,与a与b互质矛盾,故x与b一定互质。
得证。
【3.4】开始证明m' == m,先证此问题:对互质的两个数:a和b;在[1,a]之间,所有与a互质的数x,计算出的y = b * x mod a 均不相等。
即:所有的y与x一一映射,不存在两个不同的xi和xj能算出同一个y的情况。
反证法:我从[1,a]中任选与a互质的两个数字,xi和xj;若存在:b * xi mod a == b * xj mod a;则: (b * xj - b * xi) 一定是a的整数倍,相当于把相同的余数都减掉了,剩下的都是a的倍数,这个很好理解;也就意味着:b * (xj - xi) 是a的整数倍。
因为|xj - xi|必定比a小,所以,若想从b * (xj - xi)中提取出a因子,那么必须从b中提取出一个大于1的因子,乘上(xj - xi),才有可能凑出一个a因子;假设从b中提取出了一个大于1的因子m,凑出了a;那么该因子m也必定是a的因子;而a、b存在大于1的因子m,与a、b互质相矛盾,故不存在;所以b * (xj - xi) 不可能是a的整数倍。也就是说任意一对: b * xi mod a 和 b * xj mod a 均不相等。
得证。
【3.5】对于【3.4】中所有的y = b * x mod a,均与a互质。
显然,根据【3.2】:因为b与a互质、x与a互质,故b * x 与a 互质;又根据【3.3】因为b * x 与 a互质,故b * x mod a 与 a互质。
得证。
【3.6】综合【3.4】和【3.5】进行推论:
因为【3.4】中所有的y均比a小;且所有的y都是与x一一对应,没有重复,所以数量相等;且所有的y都与a互质。如果我将所有的x放入集合X,所有的y放入集合Y,那么X与Y必等价,表示的都是:所有【1,a】之间与a互质的正整数。
接着推:因为集合X和集合Y等价,所以两集合里面所有元素乘积必相等:
x1 * x2 * x3 ... x(f(a)) == y1 * y2 * y3 ... y(f(a))
肯定的,里面元素都一样,只是乘的顺序可能不相等,所以一个也不能漏。
然后将y侧都替换为:y = b * x mod a 的形式
x1 * x2 * ... == (b * x1 mod a) * (b * x2 mod a) ...
两边mod依然相等:
x1 * x2 * ... mod a == (b * x1 mod a) * (b * x2 mod a) ... mod a
根据【3.1】"模积模 等于 积的模":
x1 * x2 * ... mod a = (b * x1 mod a) * (b * x2 mod a) ... mod a = (b * x1) * (b * x2) ... mod a = b ^ f(a) * (x1*x2...) mod a
f(a)就是集合X中元素个数,也就是[1,a]中所有与a互质的数字个数。
两边mod相等 的条件等价于两边相减余下k个a,k任意;
所以有右-左有:
(b ^ fa - 1) * (x1 * x2 * ...) = k * a
因为 x1、x2 ... 均与a互质根据【3.2】(x1 * x2 *....)与a互质,所以 b ^ fa - 1 必为a的整数倍。
故:b ^ fa mod a = 1成立。【定理A】
根据这个推论,我们套入RSA中,上式 a 即为 RSA中的n, b即为RSA中的m;
那么:
c = m ^ e mod n;e * d mod fn = 1;
有:m' = m ^ (e * d) mod n = m ^ (k * fn + 1) mod n = m * (m^fn * m^ fn ... *m^fn) mod n
根据【3.1】"积的模 等于 模积模":
m' ={ m mod n * [ (m^fn mod n) *...] } mod n
根据前面的【定理A】,m ^ fn mod n = 1,所以上式中括号中是一堆1相乘,故:
m' = {m mod n * (1 * 1 * 1 ...)} mod n = m mod n
因为RSA在截取密文m时,始终保证m < n,故 m mod n = m,即m' = m。
得证:解密文 m' == 原文m;RSA加解密成立。
上述【定理A】即为:欧拉定理,其核心就是证明所有x凑成的集合X 与 所有y = b * x mod a 组成的集合Y,完全等价,所以元素相乘值相等,再将y中所有的b,单独提出,凑出b^fa形式,最后数学变换验证了:b^(fa - 1) mod a = 1成立得出。
【3.7】m与n不互质的情况
在上文【3.6】中,我们单纯套用了【定理A】,【定理A】中对a、b的要求是:a、b互质;然而,在RSA中,我们对原文m的要求仅为:m < n,并未保证互质;实际情况下:m可能出现比n小的任何数字;n = p * q;n必不是质数,因此没法保证m、n互质;但在此种情况下,RSA依然成立,以下给出证明:
考虑为数不多的几种m、n不互质的情况:由于m < n; n = p * q;p、q均为质数;那么仅有以下情况m、n不互质:
-
m = k * p; k < q;
-
m = k * q; k < p;
由于p、q等价,因此我们只需证明其中一种情况RSA仍成立,另一种情况完全对称。
取m = k * p; k < q;的情况进行讨论:
目标:计算出 c = (k * p) ^ e mod n,验证:m' = c ^ d mod n 与 m相等。
由于p、q为质数 且 k、q互质,根据【3.2】k * p 与 q互质;所以,k * p 与 q满足欧拉定理:
(k * p) ^ fq mod q = 1
那么:[ (k * p) ^ fq ] * [(k * p) ^ fq] mod q = ?
显然还是1,根据【3.1】"积的模 等于 模积模":
原式 = { [ (k * p) ^ fq mod q] * [ (k * p) ^ fq mod q] } mod q = (1 * 1) mod q
那是不是我任意个 (k * p) ^ fq 相乘 mod q都是1,所以fp个相乘也是1,故有:
(k * p) ^(fp * fq) mod q = 1
fp = p - 1; fq = q -1; fn = (p - 1) * (q - 1);所以:
(k * p) ^ fn mod q = 1;意味着:
(k * p) ^ fn = s * q + 1 ; s任意,不知道多少。
两边同时乘 k * p:
(k * p) ^ (fn + 1) = s * q * k * p + k * p
两边 mod n:
(k * p) ^ (fn + 1) mod n = k * p
根据RSA,e * d mod fn = 1;故:e * d = s * fn + 1 ; s任意;
结合上式:我现在需要根据 :
(k * p) ^ (fn + 1) mod n = k * p
推出:
(k * p) ^ (s * fn + 1) mod n = k * p
怎么办,如果我说我能根据:
(k * p) ^ (fn + 1) mod n = k * p
推出:
(k * p) ^ (2 * fn + 1) mod n = k * p;
是不是意味着我把2换成s;s >= 1;是成立的:
那就拆:
(k * p) ^ (2 * fn + 1) mod n = [ (k * p) ^ fn * (k * p)^(fn + 1)] mod n = [ (k * p) ^ fn mod n ] * {[(k * p)^(fn + 1)] mod n} mod n
后面那个fn + 1的项是不是就等于 k * p:
原式 = [ (k * p) ^ fn mod n ] * (k * p) mod n
因为k * p 一定比 n 小,所以k * p = k * p mod n
原式 = [ (k * p) ^ fn mod n ] * (k * p mod n) mod n
再把它合进去:
原式 = [ (k * p) ^ fn * (k * p) mod n ] mod n = (k * p) ^ (fn + 1) mod n = k * p
是不是前面不论再乘多少个 (k * p) ^ fn,我都能这样把它吃掉,故:
m' = (k * p) ^ (s * fn + 1) mod n = (k * p) ^ (fn + 1) mod n = k * p = m
所以,RSA在m与n不互质的情况下,依然成立。
【4】总结
通过重新叙述RSA加解密流程,进行总结:
- 通过寻找两个大质数p、q,保证fn = fp * fq;fp、fq表示[0,p/q]之间与p/q互质的数字个数,由于p、q为质数,保证了:fp = (p - 1) ; fq = (q - 1);fn = (p - 1) * (q - 1)成立(f函数叫欧拉函数,这个函数有一些特殊性质,本文【1】证明的是其中一个:p、q为质数情况下的性质,f(p * q) = fp * fq)。
- 取质数e作为公钥,并根据e、n使用辗转相除求解私钥d,【2】阶段证明,并列举了上下台阶的例子便于理解为什么使用辗转相除,其本质并非求解最大公约数。
- 对原文m进行加密,c = m ^ e mod n
- 对密文c进行解密, m' = c ^ d mod n = m;包含两种情况:1.m与n互质,套用欧拉定理得证;2.m与n不互质,【3.7】阶段给出证明。两种情况下:m' = m 均成立,故仅保证m < n 即可。
结束,以上就是RSA所依赖理论基础的全部证明过程;本文的所有论证均从最基础的理论出发,是为了保证在不了解欧拉函数、欧拉定理、辗转相除用意的前提下依然能理解RSA加解密成立的原因。同时,也填补实际使用时对产生的:e、n不互质,m、n不互质情况下的处理办法,不失为一篇全面理解RSA的好文章啊,哈哈哈;当然,对于一些已经比较了解RSA的工程师来说,本文的部分讨论着实显得拖沓了。