更多细节的理解RSA算法
一、概述
RSA算法是1977年由Ron Rivest、Adi Shamir 和 Leonard Adleman三人组在论文A Method for Obtaining Digital Signatures and Public-Key Cryptosystems提出的公钥加密算法。由于加密与解密使用不同的秘钥,从而回避了秘钥配送问题,还可以用于数字签名。该算法的诞生很大程度上有受到了论文New Directions in Cryptography(由Whitfield Diffie和Martin Hellman两人合作发表)的启发,关于RSA诞生背后的趣事见RSA 算法是如何诞生的。
原论文可以说思路非常清晰、浅显易懂,是学习该算法非常不错的英文资料。原文首先梳理了公钥加密系统和数字签名的特性和需满足的要求(这部分是实际上是借鉴了Whitfield Diffie和Martin Hellman的思想);然后阐述如何利用不同的加密秘钥和解密秘钥实现加解密流程,这是RSA算法工作的核心部分;接着介绍其背后的数学原理并证明算法的正确性,主要涉及基础数论知识(例如欧拉函数,费马定理,欧拉定理等);为了使算法更具有操作性,还介绍了如何利用"反复平法"算法进行快速的计算幂取模,从而快速的进行加解密运算,以及算法中涉及到其他参数选取(例如大素数$p,q$选取,$e$和$d$的选取和计算);同时也用一个简单的小实例模拟算法的流程;最后一个重要的话题是讨论该算法的安全性,通过考虑不同方法尝试破解该算法,并从计算量的角度解释了破解该算法的困难程度,以及其他的细节bla bla bla。。。。
所以原论文几乎是比较全地阐述了RSA算法的方方面面且不失可读性,非常值得借鉴。一些中文书籍也对该算法做了浅显易懂的解释,比如密码编码学与网络安全原理与实践和图解密码技术两本书的相关章节,都是很好的学习资料。
二、公钥加密系统
公钥加密系统主要特点是采用不同的加密秘钥和解密秘钥。在该系统中每个用户均生成自己的加密秘钥和解密秘钥,其中加密秘钥需公开出去,任何人都可以获得,加密秘钥也称为公钥;解密秘钥必须保密妥善保管,解密秘钥也称为私钥。在进行通信的时候,发送方先用接收方的加密秘钥(也就是公钥)加密消息,得到密文消息,发送给接收方;接收方收到消息用自己的解密秘钥(也就是私钥)可以解密得到明文。采用这样的加解密方式从而规避了传统加密系统中秘钥配送问题。
在密码学中,加密算法往往都是公开的,尝试通过保密加密算法来获得加密安全性的做法都是不值得推荐的(此所谓隐蔽式安全)。事实上,发明一个可行的加密算法并非易事,将其公之于众经受时间和广大使用者的检验是很好的做法。加密算法在秘钥的配合使用下进行加密和解密。因此在加密算法是公开的前提下,保证加密的安全依赖于秘钥的安全性。
将加密过程(encryption)和解密过程(decryption)分别视为一种处理程序,分别用$E$和$D$表示表示。明文消息和密文消息分别用$M$和$C$表示。则公钥加密系统有如下四种特性:
(a)对于加密后的密文$C=E(M)$,对应的解密程序能够处理得到明文:$D(C)=D(E(M))=M$。
(b)加密过程$E$和解密过程$D$是容易计算的。
(c)由公开的加密程序$E$不能轻易的得到解密程序$D$,这样可以保证由$E$加密的消息只能由$D$解密。
(d)对明文消息先解密处理在做加密处理仍可以得到明文,也即保证逆向处理的可行性:$E(D(M))=M$。
特性(a)保证了加密的原始目的,如果加密后的密文不能由接受者解密还原回明文,那彼此就不能正常通信了。特性(a)和(b)在传统的对称加密系统中也是成立的。特性(d)用于数字签名,之所以能够对明文进行解密处理这实际上并不奇怪:抛开加密与机密的概念,$E$和$M$实际上就是一个从输入到输出的映射,明文和密文的概念是站在人的立场行划分的,对于计算机,无论是明文还是密文,都是比特序列,并没有其他任何差别,因此对明文做加密无非是进行一次函数运算。
三、秘钥生成与加密解密流程
1、秘钥生成
每一个用户都会生成自己的公钥和私钥,其流程如下:
1)选取两个大的素数$p$和$q$。
2)计算$p$和$q$的乘积$n=p \times q$。
3)随机选取一个与$\phi (n)=(p-1)\times(q-1)$互质的数$e$,也即是$gcd(d,(p-1)\times(1-1))=1$,应用中通常选取$e=65537$。
4)计算$e$模$\phi (n)$的模反元素$d$,也即是计算满足$e\cdot d = 1\;(mod\;\phi (n))=e\cdot d = 1\;(mod\;(p-1)\cdot (q-1))$的$d$。
5)将$(e,n)$公开作为公钥,任何人都可以获取;将$(d,n)$作为私钥,自己妥善保存。
可见,在RSA加密算法中,公钥以一个正整数对的形式出现,同理私钥也是如此。
在秘钥生成过程中,会产生如下的信息:
$p,q,n,\phi (n),d,e$
但是需要公开的信息仅仅是$e,n$两个整数,其他信息都应该严格的保密。
(以上流程实际上与原论文中有细微差别:原论文中是选取$d$然后来计算$e$,但是在现在的许多公钥证书中,$e$基本都是相同的,也就是说现在的应用中实际是选取$e$然后计算$d$。)
2、加密和解密
还是以Alice和Bob这两个密码学中的两个网红为角色,述阐RSA算法加密和解密的流程。假设Alice向Bob发起通信,且已经获取到Bob公钥对$(e,n)$。
1)Alice首先将明文分解成若干块,每个块可以表示为一个整数(也就是将长比特序列分解成若干短的比特序列,每个序列自然可表示为一个整数),以$M$表示,使得$1 \leqslant M \leqslant n-1$。为方便起见,只考虑对一个块进行加密的流程。
2)Alice用Bob的公钥$(e,n)$做如下运算,得到密文$C$,将密文通过公网通道发送到Bob。
$C = M^{e}\;mod\;n$
3)Bob收到密文,用自己的私钥$(d,n)$做如下运算,可得到明文$M$.
$M=C^{d}\;mod\;n$
至此,加解和解密的流程已经结束,流程也非常简单清晰。
用下图总结秘钥生成和加解密的流程:
接下来的问题在于Bob一定能够解密得到明文$M$吗?其原理何在呢?答案是肯定的,其原理这正是下一节需要解释的内容。
四、数学原理
要证明Bob能够解密得到明文$M$,需要用到一点简单的基础数论知识,例如欧拉函数,费马定理,欧拉定理,不过这些知识都是比较容理解的,之前已经梳理过这方面的知识,见现代密码学中的数论基础知识梳理。
为了更清晰的认识现在要做的工作,不仿将问题抽象成如下表述:
已知条件:
$p,q$是素数,$n=p \times q$
$d$与$(p-1)\cdot (q-1)$互质,且$e,d$满足$e\cdot d = 1\;(mod\;\phi (n))=e\cdot d = 1\;(mod\;(p-1)\cdot (q-1))$
$C = M^{e}\;mod\;n,1 \leqslant M \leqslant n-1$
求证:
$M=C^{d}\;mod\;n$
证明:
由$e\cdot d = 1\;(mod\;\phi (n))$得$e\cdot d=k\phi (n)+1$($k$为某正整数)。
将$C \equiv M^{e}\;(mod\;n)$简单等价变换得:
$C^{d} \equiv M^{e\cdot d} \equiv M^{k\phi (n)+1}\;(mod\;n)$
现在需要证明$M^{k\phi (n)+1} \equiv M\;(mod\;n)$即可。
(1)当$M$与$n$互质时,由欧拉定理得:
$M^{\phi (n)}\equiv M^{(p-1)(q-1)}\equiv 1\;(mod\;n)$
简单等价变换得:
$(M^{\phi (n)})^{k}\cdot M\equiv M^{k\phi (n)+1}\equiv M\;(mod\;n)$由此得证。
(2)当$M$与$n$不互质时,由于$n$的素因子分解只能是$n=p \times q$,所以$gcd(M,n)$或者是$p$或者是$q$,也即是$M=hp$或者$M=hq$。
假设$M=hq$,此时$M$必然与$p$互质。
由费马定理和欧拉函数可知:
$M^{p-1}\equiv1\;(mod\;p)$
经过简单的等价变换得:
$(M^{p-1})^{k(p-1)}\cdot M\equiv M^{k\phi (n)+1}\equiv M\;(mod\;p)$
也即是:
$(hq)^{k\phi (n)+1}= jp+hq$($h$为某正整数)
上式的左边必然是$q$的整数倍,所以右边也必然是$q$的整数倍,可以推断$jp$必然是$q$的整数倍;因为$p,q$互质的关系,可以推断$j$必然是$q$的整数倍,也即是$j=t \cdot q$,所以继续整理上式得:
$(hq)^{k\phi (n)+1}= tqp+hq=tn+hq$($t$为某正整数)
由此得到:$M^{k\phi (n)+1}\equiv M\;(mod\;n)$
同理,$M=hp$时可以得到相同的结论。
综合(1)和(2)可以得出结论:$M^{k\phi (n)+1}\equiv M\;(mod\;n)$总是成立的,所以$M=C^{d}\;mod\;n$也是成立的,证毕。
以上证明过程(2)会比较难理解一点,原论文中对这个证明过程的处理会比较晦涩一点,但基本上也是以上证明思路,所以本文特地针对该证明尽量细化处理。
五、一个具体的实例
以下用一个比较具体的实例来模拟该算法。
1)首先Bob生成公钥和私钥
参数选取$p=887,q=911,n=p \times q=808057,\phi (n)=(p-1)(q-1)=806260,e=65537$。
利用扩展欧几里德算法求$65537d-y\phi (n)=1$,得:$65537\times 158233+(-12862)\times 806260=1$,所以求得$d=158233$。
于是Bob的公钥为$(e,n)=(65537,808057)$,私钥为$(d,n)=(158233,808057)$。
2)Alice用Bob的公钥加密明文消息$M=123456$得到密文,$C=M^{e}\;mod\;n=123456^{65537}\;mod\;808057=147690$,将$C$发送给Bob。
3)Bob收到密文消息,用自己私钥解密,$M=C^{d}\;mod\;n=815453^{158233}\;mod\;808057=123456$,Bob解密成功。
实际上,真实应用中的$n$很大,往往是1024位的二进制数(大约相当于$1024\times log_{10}2 \approx 309$位十进制数),或者是2048位,甚至是4096位的大数。
六、更多的细节
以上内容大致解释了该算法的流程和原理,但实际上还有很多的细节值得思考,比如加解密如何快速计算幂取模,$p,q$两个大素数如何有效选取,模反元素$d$如何计算等等,这些细节的处理的对该算法的实践性很重要。
1、快速幂取模算法
加密和解密过程实际上都是幂取模的操作,也即是计算:$a^b\;mod\;n$。实际应用中各参数都是极大的整数,能够找到一种高效的算法呢?
确实有一种算法能够比较高效计算幂取模,称为“反复平方”算法,在算法导论中也有描述该算法。其算法流程如下:
反复平方法的Demo实现:
public static long doModularExponentiation(long a, long b, long n) { long digit = Long.toBinaryString(b).length();// 得到b的二进制表示位数 long mask = 1 << (digit - 1); long remainder = 1; for (long i = digit - 1; i >= 0; i--) { remainder = (remainder * remainder) % n; if (mask == (b & mask)) {// 当该位为1时,需要补充考一次a remainder = (remainder * a) % n; } mask >>= 1; } return remainder; }
mask的作用就是判断指数$b$的二进制表示中当前轮次所对应的二进制位(从高位算起)是否为1,每轮过后mask会右移一位。
例如求$147690^{158233}\;mod\;808057$,在doModularExponentiation(147690,158233,808057)计算过程中,指数$b$和各轮次mask的二进制表示如下:
100110101000011001 100000000000000000 10000000000000000 1000000000000000 100000000000000 10000000000000 1000000000000 100000000000 10000000000 1000000000 100000000 10000000 1000000 100000 10000 1000 100 10 1
2、挑选大素数和素数测试
素数的分布规律尚未被完全研究清楚,但是已经有一些有意思的结论,例如素数定理表明:
$\lim_{n \to \infty }\pi (n)=\frac{n}{ln(n)}$
其中$\pi (n)$为素数分布函数,表示小于等于$n$的素数的个数。
当$n$很大时,$\frac{n}{ln(n)}$的结果比较接近真实值。定理表明随机选取一个整数为素数的概率为$\frac{1}{ln(n)}$,从概率分布的角度,可以认为$ln(n)$中就有一个是素数。因此要找一个长度与$n$相同的素数,大约需要检测$n$附近的$ln(n)$个整数就能找到,比如要找一个2048二进制位的素数,可以检测$ln2^{2048}\approx 1420$个随机选取的2048位整数的素性即可。
测试素性是一个比挑选素数更加复杂的工作,费马测试是一个基本的思路,但尚有瑕疵,会存在误判的情况(例如$7^{560}\equiv 1\;(mod\;561)$,但是561是一个合数,561=3*11*17,还有像341,645,1105等,被这样误判的数称为Carmichael数);Miller-Rabin测试精确度会更高。事实上,测试大素数是一个概率性的工作。
3、挑选$e$和计算$d$
实际应用中很多公钥证书中的$e$通常选择$(010001)_{16}=(65537)_{10}$。
由$e$计算$d$并非难事,由于$ed\equiv\;1(mod\;\phi(n))$,也就是解线性方程$ed+(-y)\phi (n)=1=gcd(e,\phi (n))$,利用扩展欧几里德算法可以快求解。
扩展欧几里德算法Demo实现:
public static long[] gcdExt(long a,long b){ long ans; long[] result=new long[3]; if(b==0) { result[0]=a; result[1]=1; result[2]=0; return result; } long [] temp=gcdExt(b,a%b); ans = temp[0]; result[0]=ans; result[1]=temp[2]; result[2]=temp[1]-(a/b)*temp[2]; return result; }
七、数字签名
数字签名是纸质签字的电子化。
数字签名涉及两个问题:首先消息的接受者要能够确认消息来源,也就是确认该消息确实是所期望的人发送的;其次接受者也能够说服第三方来验证此消息确实是签名者发送的而并非是其他人伪造的签名,这样可以反驳发送者的否认行为。数字签名必须同时是消息依赖(能够识别篡改)和签名人依赖的。数字签名其重要意义在于特定的签名者与特定的消息绑定在一起。
数字签名依赖于特性(d),加密算法会作用在未加密的明文消息上,其实就是加密过程的逆向使用。
所以签名$S$实际上就是由如下规则计算得出:
$S=D(M)$
这样Bob将这个数字签名发送给Alice,Alice可以根据Bob的公钥,验证确实是Bob签名的,同时,Alice也可以让第三方来验证这个签名,因为这个签名只能由Bob的私钥计算得出,所以Alice和别人不能伪造签名,因此Bob也就无法否认。
八、破解
由于秘钥生成过程中,只对外公开公钥对$(e,n)$,所以破解者只能尝试从这两个信息试图去计算得出私钥$(d,n)$。
破解RSA算法可以从如下几个方面做尝试:
(1)素因子分解$n$
(2)在不分解$n$的前提下计算$\phi (n)$
(3)在不分解$n$和不计算$\phi (n)$的前提下,暴力破解$d$
然而以上问题并不容易,都没有十分高效的算法求解。
理论上,只要秘钥空间是有限的,花费大量计算和大量时间都是可以破解的,然而只要秘钥长度选取足够,在现实的时间破解该算法变得不太可能,因此RSA的安全性其实是在讨论一个时效性的问题。随着硬件计算速度的升级,以前被认为足够安全的秘钥位数会被逐渐攻破,不得不进一步增加秘钥的长度,因此RSA算法的秘钥长度普遍比对称加密和椭圆曲线加密的秘钥长度更长。也就是说加密算法的安全等级并非完全取决于秘钥的长度,与加密算法本身的特性和原理有很大的关系。
九、其他
由于对公钥的获取过程可能会存在中间人攻击,导致收到的公钥不是对方的公钥而是中间人自己的公钥,对消息的机密性造成威胁。为避免这样中间人攻击,目前普遍采用公钥认证的方式,即由权威认证机构颁布公钥证书,将公钥的信任问题交由可信度更高认证机构(CA)去核实,而认证机构本身的可信度由公信力作为担保。
RSA公钥加密算法在运算速度上比对称加密慢,因此实际运用中并不会用来真正加密消息,而是用作数字签名和或者在混合密码系统中与对称加密配合使用。
在混合密码系统中组合使用对称加密和公钥加密算法:
(1)发送者产生临时秘钥,称为会话秘钥,用对称加密的算法加密消息。
(2)发送者用公钥加密算法配合接受者的公钥加密会话秘钥,然后加密后的消息与加密后的会话秘钥组合发送给接受者。
(3)接受者分理出加密后的会话秘钥,并用自己的私钥解密处会话秘钥,然后用会话秘钥在解密加密消息。
十、References
1、A Method for Obtaining Digital Signatures and Public-Key Cryptosystems
2、密码编码学与网络安全原理与实践
3、图解密码技术
转载请注明原文出处:https://www.cnblogs.com/qcblog/p/9011834.html