密码学笔记(2)——RSA密码
上一篇笔记中讲述了大量的代数知识,这一篇中我们看看如何将这些代数知识应用到RSA密码体制中。
一、公钥密码学简介
在经典密码学的研究模型中,我们根据已选择的秘钥K得到一条加密规则ek和一条解密规则dk,在这些密码体制中,dk和ek相同或者容易从ek导出,因此两者只要泄露一个就容易导致系统的不安全性。这类密码体制称为对称秘钥体制。
对称密钥体制还有一个缺点就是Alice和Bob在传输密文之前需要商定好一个共同的密钥,而且还要通过安全信道交换这个密钥。这是难以做到的,比如Alice和Bob的物理距离非常远的时候就难以实现安全信道,因而,我们需要一个新的密钥体制来解决这个问题。
公钥密码体制的想法是:通过该密码体制,使得给定的ek下求dk是计算上不可行的。这样子的话,加密规则ek就可以公开,又称为公钥。这个体制的优点是Alice可以用公钥发出加密信息给Bob,无需事先发送密钥K,Bob利用唯一的私钥dk进行解密的人。在本章的例子中,RSA密码体制及其变种是基于数学上分解大整数的困难性。下一章的例子ElGamal密码及其变种是基于离散对数的问题。
一个很重要的事实是,公钥密码体制无法提供无条件安全性,因此只研究它的计算安全性。比如说,大整数分解是计算上难以实现,不代表不可以实现。
通常我们把公钥密码体制抽象为一种称为陷门单向函数的抽象。一个函数容易计算但是难于求逆,我们称之为单向函数。但是验证单向函数是非常困难的,目前只能近似认为某些单射函数是单向函数。
Exm1 (被认为是单向函数的例子)假定n为两个大素数p和q的乘积,b为一个正整数,定义f:Zn⟶Zn为f(x)=xbmodn且当gcd(b,ϕ(n))=1时可以作为RSA的加密函数。
有了近似单向函数还不够,为什么只有Bob能解读Alice用公钥加密的密文呢,很显然Bob比其他人多知道一些信息,这个称为陷门,它包含容易求出ek的逆函数的秘密信息,这就对单向函数有一定的要求,称这类单向函数为陷门单向函数。下面就开始讨论RSA的密码体制。
二、RSA密码体制
在RSA的密码体制中,我们的计算是基于Zn的,并且令n=pq,其中p和q是两个奇素数,于是有ϕ(n)=(p−1)(q−1)。
Thm2 (RSA密码体制)设n=pq,其中p和q是素数,其中令明文空间P、密文空间C满足P=C=Zn,且定义K={(n,p,q,a,b):ab≡1(modϕ(n))}对于K=(n,p,q,a,b),定义eK(x)=xb(modn)和dK(y)=ya(modn)
(x,y∈Zn)。值n和b组成了公钥,值p,q和a组成了私钥。
证明:由于ab≡1(modϕ(n))我们有ab=tϕ(n)+1 ∃t≥1∈Z。假定x∈Z⋆n有(xb)a≡xtϕ(n)+1(modn)≡(xϕ(n))tx(modn)≡1tx(modn)≡x(modn)这就说明加密和解密是互为逆运算。
RSA密码体制的安全性是基于相信加密函数ek(x)=xb(modn)是一个单向函数,允许Bob解密密文的关键是陷门为只有他知道n=pq这个分解,它可以计算ϕ(n)=(p−1)(q−1),然后用扩展Euclidean算法计算出解密指数a。
下面给出一种简单的生成RSA参数的方法。
Alg3 (RSA参数生成算法)
1. 生成两个大素数p和q,且p≠q。
2. n⟵pq,且ϕ(n)⟵(p−1)(q−1)。
3. 选择一个随机数b(1<b<ϕ(n)),使得gcd(b,ϕ(n))=1。
4. a⟵b−1modϕ(n)
5. 公钥为(n,b),私钥为(p,q,a)。
因此,如果攻击者想要对RSA密码体制进行攻击,那么最关键的一步是试图分解n,如果这一点做到了,计算出ϕ(n)=(p−1)(q−1),那么就可以利用b和ϕ(n)计算得到解密指数a。如果要求对RSA密码体制是安全的,这就要求n=pq充分大,使得分解它成为了一个NPC问题,目前在计算上是不可行的。文档推荐取p和q均为512比特的素数,那么n就是1024比特为的模数,使得分解这个长度的整数大大超出了现有的分解能力。
在数据结构中考虑大整数的加减乘除运算需要注意一个时间复杂度的问题,这里的模n运算也需要同样的估计时间复杂度,直接利用数据结构中得到的结果,我们得到。
Thm4 (大整数运算的时间复杂度)假定x和y分别为k位和l位二进制表示的正整数,且k≤l,那么下列运算的时间复杂度列举为:
(1) x+y的时间复杂度为o(k)
(2) x−y的时间复杂度为o(k)
(3) xy的时间复杂度为o(kl)
(4) x/y的时间复杂度为o(l(k−l));o(kl)是一个弱估计
(5) gcd(x,y)的时间复杂度为o(k2
PS:第(4)条中利用Booth算法可以得到。
于是,模n运算中也有类似的一些结论。
Thm4' (大整数模n运算的时间复杂度)假定n为一个k比特整数,且0≤ m_{1},m_{2} \leq n-1$,设c为一个正整数,有如下结果。
(1) m1+m2(modn)的时间复杂度为o(k)。
(2) m1−m2(modn)的时间复杂度为o(k)。
(3) m1m2(modn)的时间复杂度为o(k2)。
(4) m−11(modn)的时间复杂度为o(k3)。
(5) mc1(modn)的时间复杂度为o((logc)×k2)。
在RSA密码体制中,计算xc(modn)可以通过c-1次模乘来实现,当c非常大时效率会很低下,注意到c可能跟ϕ(n)−1一样大,而ϕ(n)−1几乎与n一样大,且相对于k是指数阶大的。
下面将要介绍的平方-乘算法可以把计算xc(modn)所需要的模乘次数降低为最多2l次,其中l是c的二进制比特数,时间复杂度变为o(lk2),如果假定c<n,那么整个RSA加密和解密都可以在时间o((logn)3)的时间内完成,这是关于一个明文或密文字符的比特数的多项式函数。
Alg5 (Square-and-Multiply Algothrim)假定指数c用二进制表示,即c=l−1∑i=0ci2i其中ci=0或1,0≤i≤l−1,计算如下。
Square-and-Multiply(x,c,n) z = 1 for i = l-1 downto 0 z = z^2 mod n if ci=1 z = (z · x) mod n end end return zps:有时间再把它翻译成c++代码。
三、素性检测
上面所有的算法以及讲述的细节中都有一个很关键的地方没有提到,那就是如何生成大的“随机素数”,一个通常的方法是先生成一个大的随机整数,然后检测他们的素性。
2002年的时候学者证明了存在一个素性检测的多项式时间确定算法,而在实际的应用中,素性检测主要利用随机多项式时间Monte Carlo算法,比如Solovay-Strassen算法或者Miller-Rabin算法,它们可以对于整数n,可以在log2(n)的多项式时间内完成其素性检测,但是有一定的概率,算法有可能将一个合数n断言为一个素数,只不过在运行足够多次以后,错误概率可以降低到任何所期望的值以下。
另外一个问题是需要检测多少个随机整数(特定长度)才能找到一个素数,在数论中有一个著名的结果。
Thm5 (素数个数定理)假定π(N)为小于等于N的素数的个数,那么π(N)≈Nln(N)
根据这个定理,如果从1 N中随机选取一个整数p,那么它为素数的概率大约是1ln(N)。计算可知,对于1024比特的模数n=pq,其中p和q选取为512比特的素数。一个随机的512比特的整数为素数的概率大约为1ln(2512)≈1355,当然,这里面还要先排除掉一半的偶数,因此概率为2355,这在实际的算法中是可行的。下面给定关于随机算法的概念。
Def6 (偏是(否)的随机算法)对于一个判定问题的一个偏是的Monte Carlo算法是指它具有下列性质的一个随机算法:一个"是"回答总是正确的,但一个"否"回答也许是不正确的,类似的可以定义一个偏否的Monte Carlo算法。我们说一个偏是的Monte Carlo算法具有错误概率ϵ,如果算法对任何回答应该为“是”的实例至多以ϵ的概率给一个不正确的回答“否”。
PS:定义里面说的比较拗口,利用假设检验的观点,也就是随机算法犯第一类错误的概率(拒真概率)至多为ϵ,就很容易理解了。
素数问题是指对于给定的正整数n,问其是否为素数,对应的有合数问题。据此,我们来评价Solovay-Strassen算法,它对于合数问题是一个偏是的Monte Carlo算法,该算法具有12的错误概率,而对于Miller-Rabin算法,它也是一个偏是的Monte Carlo算法,错误概率为14,在具体介绍这两个算法之前,还需要一些数论的背景知识。
Def7 (模p的二次剩余)假定p为一个奇素数,a为一个整数。a定义为模p的二次剩余,如果a≡0(modp),且同余方程y2≡a(modp)有一个解y∈Zp。a定义为模p的二次非剩余,如果a≠0(modp),且a不是模p的二次剩余。
上面的定理告诉我们,如果想要求出模p的二次剩余,可以求1 p−1的平方模p值,看看值域是哪些数就可以了。但是在p很大时,计算这些数的平方模p值的计算量是非常大的(尽管有平方-乘算法),下面的Euler准则给出了一个求二次剩余的多项式时间的算法。
Thm8 (Euler准则)设p为一个奇素数,a为一个正整数。那么a是一个模p二次剩余当且仅当ap−12≡1(modp)
证明: 假定a≡y2(modp),p为一个素数,由Fermat小定理可以知道,当a≠0(modp)时有ap−1≡1(modp)成立,于是有ap−12≡(y2)p−12(modp)≡yp−1(modp)≡1(modp)
反过来,假定ap−12≡1(modp),设b为一个模p的本原元素(即它的阶为p-1,可以理解为生成元),那么有a≡bi(modp)对于某个正整数i成立,我们有ap−12≡(bi)p−12(modp)≡bi(p−1)2(modp)
由于b的阶为p−1,因此必然有p−1整除i(p−1)2(modp),从而有i为偶数(否则与假设矛盾),得到a的平方根为±bi2(modp)
Def9 (Legendre符号)假定p是一个奇素数,对任何整数a,定义Legendre符号⟮ap⟯如下:⟮ap⟯={0a≡0(modp)1a是一个模p二次剩余−1a是一个模p二次非剩余
Thm10 假定p是一个奇素数,那么⟮ap⟯≡ap−12(modp)
再来定义一个Jacobi符号,它是Legendre符号的一般形式。
Def11 (Jacobi符号)假定n为一个奇正整数,且n的素数幂因子分解为n=k∏i=1peii设a为一个整数,那么Jacobi符号⟮an⟯定义为:⟮an⟯=k∏i=1(api)ei
有了这两个符号,那就可以开始介绍Solovay-Strassen算法和Miller-Rabin算法了。
Alg12 (Solovay-Strassen算法)
Solovay-Strassen(n)
随机选取整数a,使得1≤a≤n−1
x ⟵⟮an⟯
if x=0
then return("n is composite")
y ⟵an−12(modn)
if x≡y(modn)
then return ("n is prime")
else return ("n is composite")
然而,如果要使用Solovay-Strassen算法,就必须先计算出Jacobi符号,然而如果能计算Jacobi符号,就意味着n的因子分解已经知道,说明n不是素数。因此需要一些数论结果,无需分解n就可以求出Jacobi符号的值,这就是二次互反律的一些性质。
Thm13 (二次互反律的一些性质)
(1) 如果n是一个正奇数,且m1≡m2(modn),那么⟮m1n⟯=⟮m2n⟯
(2) 如果n是一个正奇数,那么⟮2n⟯={1n≡±1(mod8)−1n≡±3(mod8)
(3) 如果n是一个正奇数,那么⟮m1m2n⟯=⟮m1n⟯⟮m2n⟯特别的,如果m=2kt且t为一个级数,那么⟮mn⟯=⟮2n⟯k⟮tn⟯
(4) 如果m和n是正奇数,那么⟮mn⟯={−⟮nm⟯m≡n≡3(mod4)⟮nm⟯其他
利用这些性质会比较轻易的求出一些Jacobi符号,实际计算中,会发现有这么一种现象。假定n>1为奇数,如果n是素数,对于任一整数a有⟮an⟯≡an−12(modn),但是当n为合数时,这个式子可能成立也可能不成立,如果同余式成立,那么称这样子的合数为Euler伪素数。容易看到,⟮an⟯=0当且仅当gcd(a,n)>1
下面通过概率论的一些知识回答一个问题,假定我们已经生成了随机数n,且用Solovay-Strassen算法检测完其素性,如果运行了m次,就能有多大程度上相信n是一个素数,换言之,给定事件A表示“一个特定长度的随机奇整数n是合数”,事件B表示“算法连续回答了m次'n是一个素数'”,我们要求的是这样一个概率P(A|B),即在算法的回答下,我们推测这个数犯错误的概率。
显然直接求这个条件概率P(A|B)是比较困难的,但是条件概率P(B|A)是比较好求的,它表示在n为合数的情况,算法判断错误的概率,估计值为P(B|A)≤2−m,这两个概率明显是不一样的,基于我们是否知道n的素性。这两个概率的关系可以用贝叶斯定理表示,为了求这个概率,还需要P(A),这就需要素数定理了。假定N≤n≤2N,从素数定理可知,在N和2N的奇素数大约是2Nln2N−NlnN≈NlnN≈nlnn由于在N和2N之间的奇数的个数为N/2≈n/2,估计一下有P(A)≈1−2lnn接着就可以做如下的计算了。
P(A|B)=P(B|A)P(A)P(B)=P(B|A)P(A)P(B|A)P(A)+P(B|¯A)P(¯A)≈P(B|A)⟮1−2lnn⟯P(B|A)⟮1−2lnn⟯+2lnn=P(B|A)(lnn−2)P(B|A)(lnn−2)+2≤2−m(lnn−2)2−m(lnn−2)+2=lnn−2lnn−2+2m+1
利用数值计算就可以知道,当m慢慢增大时,概率会慢慢趋近于0,而且收敛得比较快的。
下面来考虑Miller-Rabin算法。
Alg14 Miller-Rabin(n)
把n−1写成n−1=2km,其中m是一个奇数
随机选取整数a,使得1≤a≤n−1
b⟵am(modn)
if b≡(modn)
then return ("n is prime")
for i⟵0 to k−1
if b≡−1(modn)
then return ("n is prime")
else b⟵b2(modn)
return ("n is composite")
对于Miller-Rabin算法,可以证明,它对于合数问题是一个偏是的Monte Carlo算法。
Thm15 Miller-Rabin算法对于合数问题是一个偏是的Monte Carlo算法
证明:
用反证法,假设Miller-Rabin算法对某个素数n回答了"n为合数",我们首先可以知道am≡1(modn)(否则直接return "n is prime"),在下面的for循环中,看到else一行发现检测的值为序列am,a2m,…,a2k−1m,由于最后的回答为n为合数,那么对于0≤i≤k−1,均有a2im≠−1(modn)
现在利用n为素数的假定,由n−1=2km,利用Fermat定理得到a2km≡1(modn)
那么得到a2k−1 m是模n的1的平方根,由于n为素数,仅有两个模n的1的平方根,即±1(modn),我们有a2k−1 m≠−1(modn)(否则returnn为素数)
从而得到a2k−1 m≡1(modn)
重复上述过程,就会得到am≡1(modn)
在这种情形下,算法会回答"n为素数",推出矛盾。
最后还要讨论一个问题,就是关于模n的平方根的问题,就是考虑这样子的一个问题。假定n为一个奇数,并且gcd(n,a)=1,考虑同余方程y2≡a(modn)中具有y∈Zn的根的个数,前面可以看到,当n为素数时,同余方程要么没有解,要么有两个解,为⟮an⟯=−1或者⟮an⟯=1,下面给出它的一个推广。
Thm16 假定p为一个奇素数,e为一个正整数,且gcd(a,p)=1,那么同余方程y2≡a(modpe)当⟮ap⟯=−1时没有解,当⟮ap⟯=1时有两个解(模pe)
进而将奇素数推广到奇整数的情形。
Thm17 假定n>1为一个奇数,且有如下分解n=l∏i=1peii其中pi为不同的素数,且ei为正整数,进一步假定gcd(a,n)=1,那么同余方程y2≡a(modn)当⟮api⟯=1对于所有的i∈{1,2,3,…,l}成立时有2l个模n的解,其他情形没有解。
证明:
容易知道y2≡a(modn)当且仅当 y2≡a(modpeii)对于所有的i∈{1,2,…,l}成立,否则如果对某个i有⟮api⟯=−1,那个对应的同余方程y2≡a(modpeii) 没有解,导致整个方程没有解。
再由Thm16可知道每一个同余方程有y2≡a(modpeii)有两个模peii的解,设为bi,1和bi,2,令bi∈{bi,1,bi,2},那么对于同余方程y≡bi(modpeii)有唯一的模n解,可以利用中国剩余定理求出,由于有2l方式选择,故对应有2l个模n的解。
最后,假定x2≡y2≡a(modn),其中gcd(a,n)=1,设z=xy−1(modn),得到z2≡1(modn)。反过来,如果z2≡1(modn),那么有(xz)2≡x2(modn)对于任意x成立,因此,把a∈Z⋆n的某个给定平方根与1的2l平方根做出2l个成绩,就得到了a的2l个平方根。
下一篇读书笔记应该不会那么长了,会专门介绍一些关于分解因子的算法。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步