RSA算法再讨论--从理论到实践

在第9节的最后,我们提到过如下定理

1、设|a|=m,则对于任意正整数n,an=1←→m|n
2、G为有限交换群,G中元素的最大阶=n,则G中任一元素的阶整除n

注意到乘法群Zn*也是有限交换群,所以以上定理也成立。
可以证明:Zn*中所有元素的最大阶是p-1与q-1的最小公倍数,简记为LCM(p-1,q-1)。
不熟悉最小公倍数概念的同学赶紧去找一本小数数学书复习。
根据上述定理,对于任意与n互素的数a, a|a|≡aLCM(p-1,q-1)≡1(mod n)成立
可以仿照上一节公私钥对选择的思路,找到使d*e≡1(mod LCM(p-1,q-1))成立的一对数(e,d),公开e作为公钥,保留d作为私钥
这样在某种意义上,得到同一个公钥e对应的两个私钥
(因为对于与n有公因子的数a,我们还未验证aLCM(p-1,q-1)≡1(mod n)是否成立,我们将此问题留给读者)

现已知,对于RSA算法的安全性(即私钥的安全性),取决于对模n的整数分解,而且n越大越好
需要说明,关于RSA的安全性与整数分解困难程度的关系,以及p,q如何选择等问题上,众多学者发表了一篇又一篇的Paper
这里我们只是根据目前公认的结论,认为对其最好的攻击难度基本等同于对n进行分解
感兴趣的同学可以查下目前最快整数分解的算法复杂性,当然也不排除日后发现了对RSA算法的其他快速破解漏洞
对于本文中涉及到的其他一些算法(如DLP),也是根据同样的思路进行阐述。

现在理论有了,我们开始动手实践吧。首先碰到的一个问题就是构成n的大素数从哪里来?
或者说,对于任意一个大整数(比如说有500个十进制位长),能否判断它是一个素数
这一切,密码学家和数学家已经给我们一个满意的答案
简言之,目前有两大类素数判定方法:概率法和确定性方法(AKS算法)。它们各有千秋,请读者自己GOOGLE。
BTW,笔者的手机号码经测试,就是一个素数,有兴趣的同学可以计算一下,在现有的手机号码段中,共有多少个素数号码

知道如何判断一个大整数是素数后,我们就可以构造如下素数生成算法:
1、任意随机生成一个大整数
2、判断它是否为素数--如果是,则停止,否则返回1继续

读者自然会问,我平均要多少次操作才能使第2步停止,得到一个大整数
幸运的是,步骤1循环次数不多,经数学家的努力,我们有如下结论
用π(x)表示不超过x的素数个数,则当x足够大时,π(x)≈x/lnx,读者可以根据此公式计算300位长的整数中素数的比例大概是多少

素数的生成问题解决了,模n也得到了,如何计算大整数的模幂运算,即计算ae(mod n)=?
直接利用CPU计算是不行的,它受到其算术运算单元字长的限制,只能通过软件实现。
想自己实现大整数的读者,可以参考《密码编码学: 加密方法的C与C++实现》或《程序员密码学》
想偷懒,直接要得到计算结果的读者,可以请出轻量级的工具Perl来帮忙计算

perl -Mbigint -e "$x=Math::BigInt->bmodpow(0x2, 0xDFDF, 0x34343453); print $x->as_hex"

最后我们介绍一下重量级工具OpenSSL,这是一个著名的开源安全工具包,几乎实现了所有密码学相关的算法,同时还实现SSL/TLS协议。
其重要性不用多说,请读者到英文的wikipedia或www.openssl.cn上去查找,还可以参考《OpenSSL与网络信息安全--基础、结构和指令》
今后我们在课程中,将一直使用OpenSSL作为实例讲解。

关于RSA算法的完整实现,从素数生成到加解密操作,你都可以在OpenSSL包中找到。

最后再提一下RSA算法的解密运算,由于私钥d通常很大,常采用CRT(不是阴极射线管,是中国剩余定理)进行加速,本文就不再展开了,感兴趣的同学可以查看相关资料。