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 算法是如何诞生的。html
原论文能够说思路很是清晰、浅显易懂,是学习该算法很是不错的英文资料。原文首先梳理了公钥加密系统和数字签名的特性和需知足的要求(这部分是其实是借鉴了Whitfield Diffie和Martin Hellman的思想);而后阐述如何利用不一样的加密秘钥和解密秘钥实现加解密流程,这是RSA算法工做的核心部分;接着介绍其背后的数学原理并证实算法的正确性,主要涉及基础数论知识(例如欧拉函数,费马定理,欧拉定理等);为了使算法更具备操做性,还介绍了如何利用"反复平法"算法进行快速的计算幂取模,从而快速的进行加解密运算,以及算法中涉及到其余参数选取(例如大素数$p,q$选取,$e$和$d$的选取和计算);同时也用一个简单的小实例模拟算法的流程;最后一个重要的话题是讨论该算法的安全性,经过考虑不一样方法尝试破解该算法,并从计算量的角度解释了破解该算法的困难程度,以及其余的细节bla bla bla。。。。git
因此原论文几乎是比较全地阐述了RSA算法的方方面面且不失可读性,很是值得借鉴。一些中文书籍也对该算法作了浅显易懂的解释,好比密码编码学与网络安全原理与实践和图解密码技术两本书的相关章节,都是很好的学习资料。算法
公钥加密系统主要特色是采用不一样的加密秘钥和解密秘钥。在该系统中每一个用户均生成本身的加密秘钥和解密秘钥,其中加密秘钥需公开出去,任何人均可以得到,加密秘钥也称为公钥;解密秘钥必须保密妥善保管,解密秘钥也称为私钥。在进行通讯的时候,发送方先用接收方的加密秘钥(也就是公钥)加密消息,获得密文消息,发送给接收方;接收方收到消息用本身的解密秘钥(也就是私钥)能够解密获得明文。采用这样的加解密方式从而规避了传统加密系统中秘钥配送问题。安全
在密码学中,加密算法每每都是公开的,尝试经过保密加密算法来得到加密安全性的作法都是不值得推荐的(此所谓隐蔽式安全)。事实上,发明一个可行的加密算法并不是易事,将其公之于众经受时间和广大使用者的检验是很好的作法。加密算法在秘钥的配合使用下进行加密和解密。所以在加密算法是公开的前提下,保证加密的安全依赖于秘钥的安全性。网络
将加密过程(encryption)和解密过程(decryption)分别视为一种处理程序,分别用$E$和$D$表示表示。明文消息和密文消息分别用$M$和$C$表示。则公钥加密系统有以下四种特性:app
(a)对于加密后的密文$C=E(M)$,对应的解密程序可以处理获得明文:$D(C)=D(E(M))=M$。函数
(b)加密过程$E$和解密过程$D$是容易计算的。学习
(c)由公开的加密程序$E$不能轻易的获得解密程序$D$,这样能够保证由$E$加密的消息只能由$D$解密。测试
(d)对明文消息先解密处理在作加密处理仍能够获得明文,也即保证逆向处理的可行性:$E(D(M))=M$。ui
特性(a)保证了加密的原始目的,若是加密后的密文不能由接受者解密还原回明文,那彼此就不能正常通讯了。特性(a)和(b)在传统的对称加密系统中也是成立的。特性(d)用于数字签名,之因此可以对明文进行解密处理这实际上并不奇怪:抛开加密与机密的概念,$E$和$M$实际上就是一个从输入到输出的映射,明文和密文的概念是站在人的立场行划分的,对于计算机,不管是明文仍是密文,都是比特序列,并无其余任何差异,所以对明文作加密无非是进行一次函数运算。
一、秘钥生成
每个用户都会生成本身的公钥和私钥,其流程以下:
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$。)
二、加密和解密
仍是以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$如何计算等等,这些细节的处理的对该算法的实践性很重要。
一、快速幂取模算法
加密和解密过程实际上都是幂取模的操做,也便是计算:$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
二、挑选大素数和素数测试
素数的分布规律还没有被彻底研究清楚,可是已经有一些有意思的结论,例如素数定理代表:
$\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测试精确度会更高。事实上,测试大素数是一个几率性的工做。
三、挑选$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)接受者分理出加密后的会话秘钥,并用本身的私钥解密处会话秘钥,而后用会话秘钥在解密加密消息。
一、A Method for Obtaining Digital Signatures and Public-Key Cryptosystems
二、密码编码学与网络安全原理与实践
三、图解密码技术
转载请注明原文出处:http://www.javashuo.com/article/p-vofdbbui-kd.html