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 算法随时准备上阵。古希腊数学家对数字执着的研究,直到今天也仍然绽放着光彩。

RSA 算法的更多相关文章

  1. 信息安全-5:RSA算法详解(已编程实现)[原创]

    转发注明出处:http://www.cnblogs.com/0zcl/p/6120389.html 背景介绍 1976年以前,所有的加密方法都是同一种模式: (1)甲方选择某一种加密规则,对信息进行加 ...

  2. RSA算法原理

    一直以来对linux中的ssh认证.SSL.TLS这些安全认证似懂非懂的.看到阮一峰博客中对RSA算法的原理做了非常详细的解释,看完之后茅塞顿开,关于RSA的相关文章如下 RSA算法原理(一) RSA ...

  3. C#RSA算法实现+如何将公钥为XML格式转为PEM格式,给object-C使用

    .net中,处于安全的考虑,RSACryptoServiceProvider类,解密时只有同时拥有公钥和私钥才可以.原因是公钥是公开的,会被多人持有.这样的数据传输是不安全的.C#RSA私钥加密,公钥 ...

  4. [已解决] 快速理解RSA算法

    RSA算法基础详解 http://www.cnblogs.com/hykun/p/RSA.html RSA算法原理(一) http://www.ruanyifeng.com/blog/2013/06/ ...

  5. 公钥私钥和RSA算法

    1, RSA算法原理(一) http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html 2, RSA算法原理(二) http: ...

  6. 跨越千年的RSA算法

    转载自http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...

  7. (转)RSA算法原理(二)

      作者: 阮一峰 日期: 2013年7月 4日 上一次,我介绍了一些数论知识. 有了这些知识,我们就可以看懂RSA算法.这是目前地球上最重要的加密算法. 六.密钥生成的步骤 我们通过一个例子,来理解 ...

  8. (转) RSA算法原理(一)

    最近用到了RSA加密算法,虽然有现成的,但是想看看它的原理,翻到此文,感觉写得很好,通俗易懂,转了.   作者: 阮一峰 日期: 2013年6月27日 如果你问我,哪一种算法最重要? 我可能会回答&q ...

  9. springmvc使用RSA算法加密表单

    今天被吐槽在客户端用js对密码进行md5加密其实也不见得安全.这种做法其实不见得有什么作用,学过计算机网络都知道,在网上抓一个包是很简单的事,就算别人抓包抓不到你原始密码,用这个md5后的密码一样可以 ...

  10. RSA算法基础详解

    . 首页 博客园 联系我 前言:在RSA诞生之前. RSA算法. 质数与互质数. 模运算. 同余. 欧拉函数. 欧拉定理与模反元素. 真实的例子. 计算密钥. 密钥组成与加解密公式. 安全性. 一点感 ...

随机推荐

  1. OpenStack Trove组件WSGI和RPC调用流程(参考调用的整个过程)

    参考博文:https://blog.csdn.net/bill_xiang_/article/details/72909927

  2. 引用全局变量global

    lang = Lang.chn def set_lang(lang_type): global lang lang = lang_type

  3. 注解_Annotation

    ---恢复内容开始--- 一.什么是注解 注解,英文Annotation,它不是程序本身,是对程序的解释,在这里我会想到为什么不能使用注释呢,因为注解是关于程序对信息的处理的流程的一些说明,而且格式也 ...

  4. python——列表入门

    学习列表先分析一段程序: list = ['zx', 'xkd', 1997, 2018] list1=list+[1,2,3]#列表拼接 list2=[list,list1] print('嵌套的列 ...

  5. leetcode198

    public class Solution { public int Rob(int[] nums) { ; ; ; k < nums.Length; k++) { int tmp = i; i ...

  6. setDaemon 守护线程

    setDaemon(True): 将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起.这个方法基本和join是相反的. 当我们 在程序运行中,执行一个 ...

  7. 【Java】获取二维数组行列长度

    二维数组int array[][] = new int[3][3]; 行长度:array.length 列长度:array[i].length

  8. python 的包的导入

    已经写过一篇包的导入了,最近又遇到了点问题,所以想把这些再搞的明白点就又试了试 代码结构如下 在test目录下,有Admin包,home包,在home下有它的子包foo 各个文件代码如下 admins ...

  9. 基于maven构建javaweb项目思路梳理及改进

    需要准备的东西: Jdk. myeclipse. maven包 预装jdk环境 1.maven安装及配置: a)      详见url https://www.cnblogs.com/eagle668 ...

  10. CSS定位方法