RSA 算法
RSA 算法 from http://www.matrix67.com/blog/archives/5100
所有工作都准备就绪,下面我们可以开始描述 RSA 算法了。
首先,找两个质数,比如说 13 和 17 。实际使用时,我们会选取大得多的质数。把它们乘在一起,得 221 。再计算出 (13 – 1) × (17 – 1) = 192。根据前面的结论,任选一个数 a ,它的 i 次方除以 221 的余数将会呈现长度为 192 的周期(虽然可能存在更短的周期)。换句话说,对于任意的一个 a,a, a193, a385, a577, … 除以 221 都拥有相同的余数。注意到, 385 可以写成 11 × 35 ……嘿嘿,这下我们就又能变数学小魔术了。叫一个人随便想一个不超过 221 的数,比如 123 。算出 123 的 11 次方除以 221 的余数,把结果告诉你。如果他的计算是正确的,你将会得到 115 这个数。看上去,我们似乎很难把 115 还原回去,但实际上,你只需要计算 115 的 35 次方,它除以 221 的余数就会变回 123 。这是因为,对方把他所想的数 123 连乘了 11 次,得到了一个数 X ;你再把这个 X 乘以自身 35 次,这相当于你们合作把 123 连乘了 385 次,根据周期性现象,它除以 221 的余数仍然是 123 。然而,计算 35 个 X 连乘时,反正我们要取乘积除以 221 的余数,因此我们不必完整地获知 X 的值,只需要知道 X 除以 221 的余数就够了。因而,让对方只告诉你 X 取余后的结果,不会造成信息的丢失。
不过这一次,只知道加密方法后,构造解密方法就难了。容易看出, 35 之所以能作为解密的钥匙,是因为 11 乘以 35 的结果在数列 193, 385, 577, … 当中,它除以 192 的余数正好是 1 。因此,攻击者可以求解 11x mod 192 = 1 ,找出满足要求的密钥 x 。但关键是,他怎么知道 192 这个数?要想得到 192 这个数,我们需要把 221 分解成 13 和 17 的乘积。当最初所选的质数非常非常大时,这一点是很难办到的。
根据这个原理,我们可以选择两个充分大的质数 p 和 q ,并算出 n = p · q 。接下来,算出 m = (p – 1)(q – 1) 。最后,找出两个数 e 和 d ,使得 e 乘以 d 的结果除以 m 余 1 。怎么找到这样的一对 e 和 d 呢?很简单。首先,随便找一个和 m 互质的数(这是可以做到的,比方说,可以不断生成小于 m 的质数,直到找到一个不能整除 m 的为止),把它用作我们的 e 。然后,求解关于 d 的方程 e · d mod m = 1(就像刚才攻击者想要做的那样,只不过我们有 m 的值而他没有)。 Bézout 定理将保证这样的 d 一定存在。
好了,现在, e 和 n 就可以作为加密钥匙公之于众, d 和 n 则是只有自己知道的解密钥匙。因而,加密钥匙有时也被称作公钥,解密钥匙有时也被称作私钥。任何知道公钥的人都可以利用公式 c = ae mod n 把原始数据 a 加密成一个新的数 c ;私钥的持有者则可以计算 cd mod n ,恢复出原始数据 a 来。不过这里还有个大问题: e 和 d 都是上百位的大数,怎么才能算出一个数的 e 次方或者一个数的 d 次方呢?显然不能老老实实地算那么多次乘法,不然效率实在太低了。好在,“反复平方”可以帮我们快速计算出一个数的乘方。比方说,计算 a35 相当于计算 a34 · a ,也即 (a17)2 · a ,也即 (a16 · a)2 · a,也即 ((a8)2 · a)2 · a……最终简化为 ((((a2)2)2)2 · a)2 · a ,因而 7 次乘法操作就够了。在简化的过程中, a 的指数以成半的速度递减,因而在最后的式子当中,所需的乘法次数也是对数级别的,计算机完全能够承受。不过,减少了运算的次数,并没有减小数的大小。 a 已经是一个数十位上百位的大数了,再拿 a 和它自己多乘几次,很快就会变成一个计算机内存无法容纳的超级大数。怎么办呢?别忘了,“反正最后都要对乘积取余,相乘之前事先对乘数取余不会对结果造成影响”,因此我们可以在运算过程中边算边取余,每做一次乘法都只取乘积除以 n 的余数。这样一来,我们的每次乘法都是两个 n 以内的数相乘了。利用这些小窍门,计算机才能在足够短的时间里完成 RSA 加密解密的过程。
RSA 算法实施起来速度较慢,因此在运算速度上的任何一点优化都是有益的。利用中国剩余定理,我们还能进一步加快运算速度。我们想要求的是 a35 除以 n 的余数,而 n 是两个质数 p 和 q 的乘积。由于 p 和 q 都是质数,它们显然也就互质了。因而,如果我们知道 a35 分别除以 p 和 q 的余数,也就能够反推出它除以 n 的余数了。因此,在反复平方的过程中,我们只需要保留所得的结果除以 p 的余数和除以 q 的余数即可,运算时的数字规模进一步降低到了 p 和 q 所在的数量级上。到最后,我们再借助“今有物,不知其数”的求解思路,把这两条余数信息恢复成一个 n 以内的数。更神的是,别忘了, ai 除以 p 的余数是以 p – 1 为周期的,因此为了计算 a35 mod p ,我们只需要计算 a35 mod (p-1) mod p 就可以了。类似地,由于余数的周期性现象,计算 a35 mod q 就相当于计算 a35 mod (q-1) mod q 。这样一来,连指数的数量级也减小到了和 p 、 q 相同的水平, RSA 运算的速度会有明显的提升。
需要注意的是, RSA 算法的安全性并不完全等价于大数分解的困难性(至少目前我们还没有证明这一点)。已知 n 和 e 之后,不分解 n 确实很难求出 d 来。但我们并不能排除,有某种非常巧妙的方法可以绕过大数分解,不去求 p 和 q 的值,甚至不去求 m 的值,而直接求出一个满足要求的 d 来。不过,即使考虑到这一点,目前人们也没有破解密钥 d 的好办法。 RSA 算法经受住了实践的考验,并逐渐成为了行业标准。如果 A 、 B 两个人想要建立会话,那么我们可以让 A 先向 B 索要公钥,然后想一个两人今后通话用的密码,用 B 的公钥加密后传给 B ,这将只能由 B 解开。因此,即使窃听者完全掌握了双方约定密码时传递的信息,也无法推出这个密码是多少来。
上述方案让双方在不安全的通信线路上神奇地约定好了密码,一切看上去似乎都很完美了。然而,在这个漂亮的解决方案背后,有一个让人意想不到的、颇有些喜剧色彩的漏洞——中间人攻击。在 A 、 B 两人建立会话的过程中,攻击者很容易在线路中间操纵信息,让 A 、 B 两人误以为他们是在直接对话。让我们来看看这具体是如何操作的吧。建立会话时, A 首先呼叫 B 并索要 B 的公钥,此时攻击者注意到了这个消息。当 B 将公钥回传给 A 时,攻击者截获 B 的公钥,然后把他自己的公钥传给 A 。接下来, A 随便想一个密码,比如说 314159 ,然后用他所收到的公钥进行加密,并将加密后的结果传给 B 。 A 以为自己加密时用的是 B 的公钥,但他其实用的是攻击者的公钥。攻击者截获 A 传出来的信息,用自己的私钥解出 314159 ,再把 314159 用 B 的公钥加密后传给 B 。 B 收到信息后不会发现什么异样,因为这段信息确实能用 B 的私钥解开,而且确实能解出正确的信息 314159 。今后, A 、 B 将会用 314159 作为密码进行通话,而完全不知道有攻击者已经掌握了密码。
怎么封住这个漏洞呢?我们得想办法建立一个获取对方公钥的可信渠道。一个简单而有效的办法就是,建立一个所有人都信任的权威机构,由该权威机构来储存并分发大家的公钥。这就是我们通常所说的数字证书认证机构,英文是 Certificate Authority ,通常简称 CA 。任何人都可以申请把自己的公钥放到 CA 上去,不过 CA 必须亲自检查申请者是否符合资格。如果 A 想要和 B 建立会话,那么 A 就直接从 CA 处获取 B 的公钥,这样就不用担心得到的是假的公钥了。
新的问题又出来了:那么,怎么防止攻击者冒充 CA 呢? CA 不但需要向 A 保证“这个公钥确实是 B 的”,还要向 A 证明“我确实就是 CA ”。
把加密钥匙和解密钥匙称作“公钥”和“私钥”是有原因的——有时候,私钥也可以用来加密,公钥也可以用来解密。容易看出,既然 a 的 e 次方的 d 次方除以 n 的余数就回到了 a ,那么当然, a 的 d 次方的 e 次方除以 n 的余数也会变回 a 。于是,我们可以让私钥的持有者计算 a 的 d 次方除以 n 的余数,对原文 a 进行加密;然后公钥的持有者取加密结果的 e 次方除以 n 的余数,这也能恢复出原文 a 。但是,用我自己的私钥加密,然后大家都可以解密,这有什么用处呢?不妨来看看这样“加密”后的效果吧:第一,貌似是最荒谬的,大家都可以用我的公钥解出它所对应的原始文件;第二,很关键的,大家只能查看它背后的原文件,不能越过它去修改它背后的原文件;第三,这样的东西是别人做不出来的,只有我能做出来。
这些性质正好完美地描述出“数字签名”的实质,刚才的 CA 难题迎刃而解。 CA 首先生成一个自己的公钥私钥对,然后把公钥公之于众。之后, CA 对每条发出去的消息都用自己的私钥加个密作为签名,以证明此消息的来源是真实的。收到 CA 的消息后,用 CA 的公钥进行解密,如果能恢复出 CA 的原文,则说明对方一定是正宗的 CA 。因为,这样的消息只有私钥的持有者才能做出来,它上面的签名是别人无法伪造的。至此为止,建立安全的通信线路终于算是有了一个比较完美的方案。
实际应用中,建立完善的安全机制更加复杂。并且,这还不足以解决很多其他形式的网络安全问题。随便哪个简单的社交活动,都包含着非常丰富的协议内涵,在互联网上实现起来并不容易。比方说,如何建立一个网络投票机制?这里面的含义太多了:我们需要保证每张选票确实都来自符合资格的投票人,我们需要保证每个投票人只投了一票,我们需要保证投票人的选票内容不会被泄露,我们需要保证投票人的选票内容不会被篡改,我们还需要让唱票环节足够透明,让每个投票人都确信自己的票被算了进去。作为密码学与协议领域的基本模块, RSA 算法随时准备上阵。古希腊数学家对数字执着的研究,直到今天也仍然绽放着光彩。