RSA加解密算法原理

来源:互联网 发布:wubi.exe安装ubuntu 编辑:程序博客网 时间:2024/05/16 06:39

本文译自http://www.muppetlabs.com/~breadbox/txt/rsa.html,作者Brian Raiter. 

This article is translated from http://www.muppetlabs.com/~breadbox/txt/rsa.html. The author is Brian Raiter. Thank Brian very much for the great text & sharing spirit.

前言:我们将介绍什么

研究素数理论的数学家们一直希望甚至可以说是渴望看到自己的研究能在数学领域之外派上用场, 而RSA加解密体系就是一个将抽象数学理论应用至现实世界的极好案例。
这篇文章解释了支撑RSA的数学理论以让大家明白它的实现原理和正确性。你只需要对这个话题感兴趣并理解一些基本代数概念就可以读懂这篇文章,比如什么是变量、乘幂、合数与素数等等。文章一开始会先介绍跟RSA相关的最基本最重要的数学概念。因为这些概念是那么的基本和重要,就算你熟知一切,但仍然建议你快速浏览一遍。 另外可能有些地方提及普通程序员都能理解的一些编程概念。
开始之前,还需要对即将使用到的数学符号作一些解释。毕竟我们可以说是在讨论一个数学话题,所以文章大部分我都会使用标准的数学符号,就算有对应的编程符号。但是在一些容易引起歧义的地方会使用编程时习惯使用的符号。比如使用*表示乘法,比如pq是变量名还是p*q呢?使用*就可以避免这个问题。又比如不是所有浏览器都能显示上标,这里使用^表示幂乘。另外因为没办法打出数学里面的三横杠同余符号,所以只能使用=表示了。文章里面的所有变量都使用一个大写字母命名。 最后,文中提及的数字大多数时候特指正整数,也叫自然数。

引言:单向陷门函数(Trapdoor function)

数学里面的函数(function)其实与编程里面的函数挺类似的,概而言之,函数就是一种缩写。比如:
        F(X) = 7 * X + 43.
如果X为3,那么F(X)就是64.所以"F(3)“就是"7*3+43"的简写。
对应C语言程序里头的函数可以写成:
        int F(int x)
        {
            return 7 * x + 43;
        }


数学家们通常也很关心一个函数的反函数,比如
        G(X)=(X-43)/7, G(64)=3.
推广来说,G(F(X))=X,那么G就是F的反函数。当然,并不是所有函数都是反函数,比如F(X)=X*0,就没有反函数,因为F总是返回0,也就无法计算出X真正的值了。 一般来说,只要给出一个反函数肯定存在的数学函数,找出其反函数并不会很难。通常,只要把函数从后到前把运算反过来,比如把原函数的减法变成加法。但难道所有可逆函数都可以这样做吗?
用编程语言来表达,假设有下面两个函数:
        int foo(int x);
        int bar(int x);

而且foo() 和 bar() 跟数学函数一样,只是做一定计算然后返回一个数值,而且他们对于同样的输入参数总是输出同样的值。 (并且假设运行程序的计算机支持无限大的整数)。在此基础上,我们假设bar()是foo()的反函数。也就是说以下命题在foo的调用参数合法的前提下总是成立。
        x == bar(foo(x))
现在,假设你知道foo的源代码,而不知道bar的。你觉得你可以写出你自己的bar()函数实现吗?
好像你应该可以,因为foo()做了什么你知道得一清二楚。你也可以随便用不同的输入调用foo()多少次都可以,并且你已经知道bar()一定存在。所以你觉得你可以写出bar()的实现来,但我们能保证一定能够重构出bar来 吗?
理论上的答案是yes。给出一个foo()这样的函数,总是可能构建出反函数来的。但是,如果我们在这个问题上再加个时间上的限制,就算是你必须在宇宙终结前找出来也行,答案可能就会发生变化。
存在一些特别的函数,它们做什么看起来也很简单,而且它们怎么做到的也很清楚,但是找到它们的反操作则几乎是不可能完成的任务。这种函数就是单向限门函数(Trapdoor function)。
1975年,Whitfield Diffie, Martin E. Hellman, and Ralph Merkle认识到某些Trapdoor function可以作为一种新的密码学的数学基础。这种密码学可以做到就算是加密方法被所有人知道,而解密方法还可以保持其秘密性。 Diffie&Hellman于1976年发布了一篇论文描述了这个方法,并且提供了一些安全性比较弱的trapdoor functions。一年后的1977年,在一个MIT的技术备忘录中,Ronald L. Rivest, Adi Shamir, and Leonard Adleman就指出了一种trapdoor function可以成为RSA密码学的基础。 接下来我们就花点时间来描述一些这个函数。

背景第一节:指数计算

下面是指数运算的简单复习,应该还记得
        N^2= N * N, N^3= N * N * N, N^4= N * N * N * N,
比如:
        2^7 = 2 * 2 * 2 * 2 * 2 * 2 * 2 = 128.
我们也知道N的E次方再乘于N也就是N的E+1次方:
        N^E * N = N^(E + 1).
例如:
        2^7 * 2 = 128 * 2 = 256 = 2^8.
同样我们也可以得出:
        N^E * N * N = N^(E + 2).
而N * N 可以写成 N^2:
        N^E * N^2 = N^(E + 2).
推而广之得到公式1:
        N^A * N^B = N^(A + B).
那么:
        N^A * N^A = N^(2*A)=(N^A)^2
再推而广之得到公式2:
        (N^A)^B = N^(A * B).
这两个公式在处理指数运算的时候非常有用。

背景第二节:模运算

几乎所有计算机程序员都是通过取余运算符(通常记为%)而熟悉模运算的。取余运算符计算出一个整数被另一个整数相除所得的余数,而不是商。例如:
        27 % 12 = 3.
数学上的模运算比编程里面的取余内涵更加丰富一点。模运算可以看作把一个无限整数长的线沿着有限整数周长的圆圈绕,落到圆上同个位置的整数则认为是等价的,也称为。那么以上比喻在数学里的表示就是
        27 = 3 (mod 12), 注:数学上一般使用三横杠的同余符号。
或者用语言描述为:
         27 同余 3, 模 12.
这个论述告诉我们,27和3对12求模是相等的。
         11 + 16 = 3 (mod 12)
模运算有时也被称为钟面运算。前面的运算式对应时钟的运转则表示,如果现在是11点,16小时后会变为3点。
我们也可以发现,模运算的加法里的数是可以被其同余数替换的,比如:
因为 16=4 (mod 12)
       11 + 16 = 11 + 4 = 15=3 (mod 12).
编程语言里的表示
       (11+16) %12 = (11+4)%12 = 15 %12 = 3
又比如:
       9835 = 7 (mod 12), 而 1176 = 0 (mod 12), 那么 9835 + 1176 = 7 + 0 = 7 (mod 12).
更加有趣的是,模运算乘法中的数也是可以被其同余数代替的,接着上面的例子:
        9835 * 1176 = 7 * 0 = 0 (mod 12)(我们可以算出9835 * 1176=11565960, 而 11565960 = 0 (mod 12)).
如果模为10,那么对10求模则可以得到整数的个位数。例如:
       37 = 7 (mod 10), 287 + 482 = 9 (mod 10), 那么 895 * 9836 = 0 (mod 10).
可以想象,模运算可以使得很大的数字运算变得小数运算,使得我们不用去计算实际的数值只需要计算这些数的余数就可以,从而保证在有寄存器大小限制的计算机中实现计算变得容易。

背景第三节:算术基本定理(Fundamental Theorem of Arithmetic)

算术基本定理表明对于所有大于1的自然数都存在且只有一种方式将其分解为素数的乘积。比如
        1176 = 2 * 2 * 2 * 3 * 7 * 7, or 1176 = 2^3 * 3^1 * 7^2.
这个定理确保不存在其它方式将1176分解成不同的素数因子。 这个定理也是为什么不把1作为素数之一的原因,因为如果把1定义为素数,那么所有自然数的素因子分解将会有无限种方式了。

背景第四节:互质数

两个数的最大公因子(缩写为GCD)定义为可以整除两个数的最大数。比如:
        GCD(15, 10) = 5, GCD(18, 10) = 2, GCD(21, 10) = 1, 而 GCD(170, 102) = 34.
另一种表述方式就是最大公因子是两个数的素因子的交集里面数的乘积:
        GCD((2^3 * 3^1 * 7^2), (2^2 * 5^1 * 7^3)) = 2^2 * 7^2, 所以 GCD(1176, 6860) = 196.
如果两个数没有其它公因子,则GCD为1,则我们说这两个数是互质的。比如21和10就是互质的。
那么所有素数跟除了它自己倍数的所有其它数都是互质的。
好,我们已经了解足够的背景知识,让我们开始进入主题。

欧拉函数(Euler's Totient Function)

欧拉函数使用希腊字母phi(音为phi)表示,定义为:
        phi(N) = 从1到N-1与N互质的数的个数
那么:
        phi(4)= 2   (1 和 3 与4互质), phi(5)= 4   (1, 2, 3 和 4 与5互质), phi(6)= 2   (1 和 5 与 6互质), phi(7)= 6   (1, 2, 3, 4, 5 和 6 与 7互质), phi(8)= 4   (1, 3, 5 和 7与 8互质) phi(9)= 6   (1, 2, 4, 5, 7 和 8与 9互质).
如果我们用C语言表示
        phi = 1;
        for (i = 2 ; i < N ; ++i)
            if (gcd(i, N) == 1)
                ++phi; 
phi(1)定义为1. 如果N为素数那么很明显 phi(N) =N - 1,因为比它小的自然数都与它互质. 如果N可以分解为两个素数因子的乘积P和Q,那么我们可以证明:
        phi(P * Q) = (P - 1) * (Q - 1), if P和Q 是素数,P不等于Q。比如:
        phi(15) = 2 * 4 = 8   (1, 2, 4, 7, 8, 11, 13, 和 14).
可以自己尝试去证明(提示:从1至P*Q-1这些数中,不与P*Q互质的数是P和Q的倍数)。
当然,P和Q必须是不等的。例如phil(9)=6, 而不是phil(9)=phi(3) * phi(3)=2*2=4.

欧拉定理(Euler's Totient Theorem)

欧拉定理是RSA算法的核心理论之一:
        如果GCD(T, R) = 1 而且 T < R, 那么 T^(phi(R)) = 1 (mod R).
用文字描述:
        如果T和R互质,且T<R, 那么T的phi(R)次方对R求余数,结果为1.
我们这里只用一些例子来验证这个定理,若T为5,R为6,那么 :
        phi(6) = (2 - 1) * (3 - 1) = 1 * 2 = 2, 那么 5^(phi(6)) = 5^2 = 25,  25 = 24 + 1 = 6 * 4 + 1, 得到 25 = 1 (mod 6).
若 2 为T 且 15为R, 可以得到:
       phi(15) = (3 - 1) * (5 - 1) = 2 * 4 = 8, 那么 2^(phi(15)) = 2^8 = 256, 而且 256 = 255 + 1 = 17 * 15 + 1, 得到 256 = 1 (mod 15).

推导变化

根据欧拉定理:
        如果GCD(T, R) = 1 而且 T < R, 那么 T^(phi(R)) = 1 (mod R).
根据之前提到的模运算的规律, 很容易得到:
        T^(phi(R)) * T^(phi(R)) = 1 * 1 (mod R),
根据指数运算的规则,
        T^(phi(R) + phi(R)) = 1 * 1 (mod R),
或者:
        T^(2 * phi(R)) = 1 (mod R).
用同样的方法我们可以得到:
        T^(3 * phi(R)) = 1 (mod R).
很明显如果我们继续下去,我们可以得到一个欧拉定理的推广的表示方式
       如果GCD(T, R) = 1 而且 T < R, 那么 T^(K * phi(R)) = 1 (mod R), K可以是任意自然数.
可以注意到K*phi(R)表示的是phi(R)的倍数,也就是可以被phi(R)的整除的数。用模运算的语言来说,K*phi(R)对phi(R)求模与0同余。
那么:
       如果GCD(T, R) = 1且 T < R,如果又有 S = 0 (mod phi(R))成立,则T^S = 1 (mod R)  
我们继续推导,把T^S=1 (mod R)式子两边乘于T,
       T^S * T = 1 * T (mod R).
也即是
       T^(S + 1) = T (mod R).
同样可以得出:
       T^(S + 1) * T = T * T (mod R),
或者:
       T^(S + 2) = T^2 (mod R).
再继续做一次:
       T^(S + 3) = T^3 (mod R),
类似地我们可以得出:
       T^(S+K)=T^K(mod R),S = 0 (mod phi(R))
也就是说我们可以得到
       T^E = T^F (mod R),当E = F (mod phi(R)).(再次强调,T < R, 而且 T 和R互质). 

重点

我们现在就要到达最重要的部分了!我们再来仔细看看上面的一个等式:
        T^(S + 1) = T (mod R).  【公式3】
我们可以发现一个有趣的现象,我们拿一个数T, 把它做一个幂运算, 然后再做一次模运算,我们可以重现T!简单来说,我们已经掌握了一个可以重现一个数值的方法。 (假设R已经知道了,并且T和R互质). 你可能在想“这有什么好玩的?”,那我们来看看。下面我们会把这个函数拆分为两个独立步骤,假设我们可以找到两个新的数P和Q,使得
        P * Q = S + 1,
对于某个S的可能值成立(回顾一下,S = 0 (mod phi(R) ).
或者,更加直接的表示方式是
        P * Q = 1 (mod phi(R))
基于P和Q的存在和公式3,我们可以得到:
       T^(P * Q) = T (mod R),
也就是:
       (T^P)^Q = T (mod R)
而这个式子是可以拆分为两个步骤完成的:
       T^P = X (mod R), 那么 X^Q = T (mod R).
好了,如果你还没有看出这个方法的价值,我们来想象一下这两个步骤是由两台独立的计算机完成的,而X是由第一台计算机通过一条不安全的电话线通道发送到第二台计算机的...

这真的有用吗?

T可以是准备发送的明文消息,P、Q和R作为加密的秘钥,进一步P和R作为公钥,而Q和R作为私钥。那么X则是被加密的信息。
我们再来看关于P和Q的最重要的等式。
        P * Q = 1 (mod phi(R))
那么可以观察到P和Q必然与phi(R)互质,否则如果P或者Q与phi(R)有一个公因子D(D>1),那么D也将会是P*Q与phi(R)的公因子,那么
        gcd(P*Q, phi(R))=D, 假设P*Q=D*M, phi(R)=D*N,M与N互质。
我们知道
       P*Q = k*phi(R) + 1, 
那么
       D*M = k* D*N+1, 有 D*(M-k*N) = 1。
而D>1, 以上式子显然不可能成立,所以 P与Q必然与phi(R)互质,这个很重要。
想象一个只有时针的钟,时针一开始指向12点整,我们让它跳着走,每次跳N小时。如果N是可以被2或者3整除(2和3是12的素数因子),那么我们可以发现时针在回到12点前总是指向一部分固定的刻度。比如N=2,时针将会按照这些步骤重复:12,2,4,6,8,10,12,2,4,6,8,10,12…
但如果N是与12互质的数,那么时针在回到12点前会覆盖所有刻度。比如N=7,时针的走法将是12,7,2,9,4,11,6,1,8,3,10,5,12,...
类似地,P,Q与phi(R)互质是非常重要的。因为如此,我们可以保证对每个可能的T值,只要把它作指数为P的幂运算,再对R求模,总是会得到不同的X值(记住当作幂运算后的求模运算的话,一个循环的长度是由phi(R)决定的)

选择密钥

我们先随便挑选两个素数U和V,然后算出乘积作为R。为什么要这样做,因为这样我们很方便可以计算得出 
        phi(R)=(U-1)*(V-1)。
其次越少因数使得分解过程变得越复杂和越费时。然后我们找到两个数P和Q使得
        P * Q = 1 (mod phi(R)).
我们就会将P和R作为公钥,Q和R作为私钥。选定这些数后U和V就可以丢掉了。

实例

我们假设U=5,V=11,那么R=55,
        phi(55) = (5 - 1) * (11 - 1) = 4 * 10 = 40。
然后我们需要找出P和Q使得 P * Q = 1 (mod 40). 当然,符合这个条件的P和Q有无穷多个。这里我们尝试找出其中一对就可以了。
第一个可以帮我们省时间的是P和Q必须和40互质,也就是说P和Q因子都不能含有2和5。
然后P不能与Q对40同余。根据T^E = T^F (mod R)(当E = F (mod phi(R))),会有T^P=T^Q。而P是R都是公钥,那只要找个Q=P(mod phi(R)),就可以根据(T^P)^Q=(T^Q)^Q =T(mod R)反推出T了。也就是使得RSA退化成了对称加密算法了,因为在这种情况下P不能被泄露。一般来说,使P和Q互质是最佳选择。我们从P=7开始,根据下面的等式来找Q。
        7 * Q = 1 (mod 40).
也就是
        7 * Q = K * 40 + 1, K为任意自然数。
第一个使等式成立的Q值是23,
       7 * 23 = 161 = 4 * 40 + 1.
现在我们有P=7作为公钥,Q=23作为私钥。
根据我们的加密算法,传送的信息T必须与R小,而且要与R互质。当然T不能为1,因为1无论作任何幂运算模运算最后结果都是1。类似地R-1也需要排除,因为R-1与-1对R=55同余。那么我们把剩下的一些数制做出以下的字符表:
  2  3  4  6  7  8  9  12  13  14  16  17  18  
  A  B  C  D  E  F  G   H    I    J     K   L    M 
  19 21 23 24 26 27 28 29 31 32 34 36 37  
   N  O  P   Q    R  S   T   U   V   W  X   Y   Z 
  38 39 41 42 43 46 47 48 49 51 52 53  
  sp  0   1   2    3   4   5   6   7   8  9   * 
我们假设要传送的信息为"VENIO" (拉丁语的 "I come"):
        V  E  N  I   O 
       31 7 19 13 21
对这些数作指数为P的幂运算然后对R作模运算。
        V:31^7 (mod 55) =27512614111 (mod 55) =26 
        E: 7^7 (mod 55) =     823543 (mod 55) =28 
        N:19^7 (mod 55) =  893871739 (mod 55) =24 
        I:13^7 (mod 55) =   62748517 (mod 55) = 7 
        O:21^7 (mod 55) = 1801088541 (mod 55) =21
所以加密后的消息为 26, 28, 24, 7, 21,也就是在字符表里的 "RTQEO"。
当"RTQEO"在对端被收到后,用这些数作指数为Q的幂运算然后对R作模运算就得到T值。
        R:26^23 (mod 55) = 350257144982200575261531309080576 (mod 55) =31 
        T:28^23 (mod 55) = 1925904380037276068854119113162752 (mod 55) = 7 
        Q:24^23 (mod 55) =  55572324035428505185378394701824 (mod 55) =19 
        E: 7^23 (mod 55) =   27368747340080916343 (mod 55) =13 
        O:21^23 (mod 55) =   2576580875108218291929075869661 (mod 55) =21
也就是 31, 7, 19, 13, 21= "VENIO"。

怎么破解RSA

假设我们已经窃听到"RTQEO",P=7和R=55也从公钥目录中被找到,我们在Q未知的情况下怎么将这个消息破解。
我们知道 P * Q = 1 (mod phi(R)),或者, 
        P * Q = K * phi(R) + 1.
等式两边除于P:
        Q = (K * phi(R) + 1) / P.
K也是未知,那我们可以暴力测试。
        (1 * 40 + 1) / 7 = 41 / 7           (不能整除)
        (2 * 40 + 1) / 7 = 81 / 7           (失败) 
        (3 * 40 + 1) / 7 =121 / 7          (失败) 
        (4 * 40 + 1) / 7 =161 / 7= 23      (这个有可能!)

每次我们找到一个可能值,就对消息进行测试,如果得到不合理的破解,我们可以继续尝试。如果23不合适,我们可以继续穷举K进行测试。因为P为7,很明显我们只需要每给7个数执行测试才有可能得到能被7整除的数。再者,因为P*Q是对phi(R)=40求模,那么Q也只需要尝试到39就可以了,所以看起来破解还是很简单和很快的。 因为R是公钥,所以phi(R)也是公开的,而且我们知道R是由两个素数乘积构成的,只要能够找到这两个素数就可以破解加密后的消息。那怎么办呢?

如何使RSA不能被破解

在上面的例子里,因为R比较小,所以暴力破解是很容易的。(当然因为我们一次加密一个字符,也有可能被采用一些统计方法被破解)。但是只要我们挑足够大的U和V来生成R。比如我们不用5和11,而是用673和24971,那么R会是16805483,phi(R)会等于16779840。(R值比较大也允许我们一次加密多个字节)寻找P和Q也不是你能够用纸笔简单能算出来的了,我用Perl从写脚本到计算出来花了不到3分钟找到可用的397和211333。
但是,用计算机还是可以在几秒内从16805483找到673和24971。所以即使是看起来够大的数,仍然是不堪一击。所以我们需要大得多的数!
当然,找到难于做素数分解的大数肯定不是一个问题,毕竟自然数可以是无限的。但是,考虑到我们要做幂运算,那么大的数能用吗?

极大指数幂值的模运算

问题在于,R越大,P和Q就会越大,而我们知道P和Q会作为指数使用!下面这个数看起来很一般:
        9^(9^9)
但它的结果有3亿5千万位长!在加密数据之前,得找到个办法来存储几T的数据才行。当然,如果是这样的话,这个加密方法也就无法使用了。
还好我们最后的结果是做模运算等到的,模运算总是能够把数明显变小。
看回我们之前的例子,我们现在要解密28,因为R=55,Q=23,所以我们要知道的是
        28^23 (mod 55) = ?
我们先来看看23的二进制值是10111,也就是
        23 = 16 + 4 + 2 + 1, or 23 = 2^4 + 2^2 + 2^1 + 2^0.
那么我们就可以把大指数拆成小的部分。
       28^23 = 28^(2^4 + 2^2 + 2^1 + 2^0)      
                   = 28^(2^4) * 28^(2^2) * 28^(2^1) * 28^(2^0)    
                   = 28^(2 * 2 * 2 * 2) * 28^(2 * 2) * 28^2 * 28      
                   = (((28^2)^2)^2)^2 * (28^2)^2 * 28^2 * 28.

这个第一眼看起来还是不怎么样。但是仔细观察还是可以发现很多运算重复了。加上最后是需要做模运算的,那么我们可以利用前面讨论过的规律先计算好这些重复计算部分的摸运算结果。
比如:
         28^2 = 784 = 14 (mod 55).
利用上面的等式,我们实际上是要求下面的结果
         28^23 = (((28^2)^2)^2)^2 * (28^2)^2 * 28^2 * 28 (mod 55),
也就有:
        28^23 = ((14^2)^2)^2 * 14^2 * 14 * 28 (mod 55).
再来一次:
        14^2 = 196 = 31 (mod 55),
也就可以简化为:
        28^23 = (31^2)^2 * 31 * 14 * 28 (mod 55).
最后在对剩下的指数计算:
        31^2 = 961 = 26 (mod 55), and 26^2 = 676 = 16 (mod 55);
可以得出:
        28^23 = 16 * 31 * 14 * 28 (mod 55).
接下来的事情就毫无悬念了
         28^23 = 16 * 31 * 14 * 28 (mod 55)      
                    = 16 * 31 * 392 (mod 55)      
                    = 16 * 31 * 7 (mod 55)      
                    = 16 * 217 (mod 55)      
                    = 16 * 52 (mod 55)      
                    = 832 (mod 55)      
                    = 7 (mod 55)
看回下面的例子,结果7跟我们直接计算指数得到的值是一模一样的。利用上面的方式,我们需要处理的数不会大过(R - 1)^2.

怎么找到很大的P和Q

神奇的模运算也可以保证我们可以找到想要的P和Q。回顾一下我们需要找到符合下面条件的P和Q
       P * Q = 1 (mod phi(R)),也就是说
       P * Q = K * phi(R) + 1, K可以为任何自然数。
当我们选择合适的P后,当然不是我们的例子里头的7那么小的数,然后我们需要找到Q,重写以上的等式:
       P * Q - phi(R) * K = 1,
其中P和phi(R)为已知数,这个等式被称为Diophantine等式。当然我们在这里只是告诉读者已经存在一些能在很短时间内求出Q的算法,不再详细阐述。

数字里的安全

我们已经知道RSA的基本原理,其中很关键的是要选择一个合适的R。R必须是两个素数的乘积,而且必须要足够大使得它很难做素数分解。那么我们要怎么样开始来寻找这些数呢?
我们知道判断一个数是不是素数是一个相对简单的事情。其中一个最著名的方法称为费马小定理( Fermat's Little Theorem),下面是其中一种适合我们使用的表示形式:
             如果P是素数,那么N^(P-1)=1(mod P)对于所有N<P都成立。
这个定理是不是让你联想起欧拉定理(Euler's Totient Theorem)?不奇怪,欧拉是第一个发布费马小定理证明的,而他的欧拉定理是费马小定理的一种推广。如果你记得对于素数P,phi(P)=P-1,那么费马小定理就变得容易理解了。
当然,这个定理只有在证明一个数是合数时才比较有用。比如
          3^14 (mod 15) = 4782969 (mod 15) = 9, 和 
          5^14 (mod 15) = 6103515625 (mod 15) = 10.
对于素数17,我们可以作以下计算:
          3^16 (mod 17) = 43046721 (mod 17) = 1, 
          4^16 (mod 17) = 4294967296 (mod 17) = 1, 
          5^16 (mod 17) = 152587890625 (mod 17) = 1, and so on.
也就是说如果我们想知道一个数是否是素数,我们可以从2开始作类似计算,只要有一个结果不是1,那么那个数就是合数。如果我们从2到P-1都得到1,那么这个数一定是素数吗?很不幸,答案是否定的。存在一些数叫做Carmichael数,他们虽然比素数少,但也存在无穷多个,都可以通过以上的测试。所以我们不能只是依赖这个测试。
幸运地是,我们还有其它可靠方法用来证明一个数是否为素数。但是任何方法也只能证明一个数可以被分解,但是可以分解为什么数对于大数来说是十分困难的。

总结



基本的事实是,为了找到一个合数的因子,我们也只能坚持用暴力破解的方式:将这个数字除以所有的素数,直到其中一个看起来是合适的。有很多方法来改善这种方法(数域筛选(Number Field Sieve)目前是最好的),但他们是复杂的,他们做的是让你缩小搜索范围。他们不足以使这个问题变得可以解决。 而且可以预见新的分解方法仍然不能把问题简化到容易解决!真正的问题是,加密和解密的算法的运行时间对于R的长度是线性的。也就是说,R中的数字的位数增加一倍也使得加解密的需要的时间加倍,选择两个素数P和Q的时间也一样。但探索R的素因子的算法的运行时间是随R的位数长度呈指数增长。
因此,如果出现一个新的技术,使得可以以一万亿倍的速度分解一个数。我们需要做的是增加R的大小,又会使得破解变得困难。当然,我们也会需要多一点的时间来发送和接收我们的信息。已经有一些人在用一些秘钥就算是用Number Field Sieve来破解,需要的能量比宇宙中已知的能量还要多。
一个例子,在写这篇文章的时候,被分解的最大的数是一个在RSA-140 挑战中使用的模,是1999年2月2日完成的。在此之前的记录是一个RSA-130的数,将它分解动用了总共1000 MIPS-年 的计算时间。 RSA-140使用的模只需要10位数长,需要的时间估计会达到两倍。 
最后,这是什么使得RSA称为陷门函数的核心因素:那就是从两个素数得到一个数和从一个数重新分解出两个素数的难度差别非常大。
如果找到一个可以快速分解素因子的快速算法,将彻底摧毁RSA的加密算法。这种可能性还没有被证明是不可能的,当然也很可能将永远也不会被发现。但是,考虑素数已研究了数千年,这个问题在过去的几十年中再次受到重视,这样的算法存在的可能性似乎微乎其微。发现这个算法将会极大改变数论的面貌,就像RSA使得加密算法大为改观。

但是 - 如果这种情况发生,也会有其他陷门函数在那里等待被发现。无论RSA的未来怎么样,限门函数已经永远改变了加密学!