rsa加密填充方式
前几天看到一句话,“我们中的很多人把一生中最灿烂的笑容大部分都献给了手机和电脑屏幕”。心中一惊,这说明了什么?手机和电脑已经成为了我们生活中的一部分,所以才会有最懂你的不是你,也不是你男朋友,而是大数据。
如此重要的个人数据,怎样才能保证其在互联网上的安全传输呢?当然要靠各种加密算法。说起加密算法,大家都知道有哈希、对称加密和非对称加密了。哈希是一个散列函数,具有不可逆操作;对称加密即加密和解密使用同一个密钥,而非对称加密加密和解密自然就是两个密钥了。稍微深入一些的,还要说出非对称加密算法有DES、3DES、RC4等,非对称加密算法自然就是RSA了。那么当我们聊起RSA时,我们又在聊些什么呢?今天笔者和大家一起探讨一下,有不足的地方,还望各位朋友多多提意见,共同进步。
RSA简介:1976年由麻省理工学院三位数学家共同提出的,为了纪念这一里程碑式的成就,就用他们三个人的名字首字母作为算法的命名。即 罗纳德·李维斯特 (Ron Rivest)、 阿迪·萨莫尔 (Adi Shamir)和 伦纳德·阿德曼 (Leonard Adleman)。
公钥:用于加密,验签。
私钥:解密,加签。
通常知道了公钥和私钥的用途以后,即可满足基本的聊天需求了。但是我们今天的主要任务是来探究一下RSA加解密的原理。
说起加密算法的原理部分,肯定与数学知识脱不了关系。
我们先来回忆几个数学知识:
φn = φ(A*B)=φ(A)*φ(B)=(A-1)*(B-1)。
这个公式主要是用来计算给定一个任意的正整数n,在小于等于n的正整数中,有多少个与n构成互质的关系。
其中n=A*B,A与B互为质数,但A与B本身并不要求为质数,可以继续展开,直至都为质数。
在最终分解完成后,即 φ(N) = φ(p1)*φ(p2)*φ(p3)... 之后,p1,p2,p3都是质数。又用到了欧拉函数的另一个特点,即当p是质数的时候,φp = p - 1。所以有了上面给出的欧拉定理公式。
举例看一下:
计算15的欧拉函数,因为15比较小,我们可以直接看一下,小于15的正整数有 1、2、3、4、5、6、7、8、9、10、11、12、13、14。和15互质的数有1、2、4、7、8、11、13、14一共四个。
对照我们刚才的欧拉定理: 。
其他感兴趣的,大家可以自己验证。
之所以要在这里介绍欧拉函数,我们在计算公钥和私钥时候,会用到。
如果两个正整数m 和 n 互质,那么m 的 φn 次方减1,可以被n整除。
其中 .
其中当n为质数时,那么 上面看到的公式就变成了
mod n 1.
这个公式也就是着名的 费马小定理 了。
如果两个正整数e和x互为质数,那么一定存在一个整数d,不止一个,使得 e*d - 1 可以被x整除,即 e * d mode x 1。则称 d 是 e 相对于 x的模反元素。
了解了上面所讲的欧拉函数、欧拉定理和模反元素后,就要来一些化学反应了,请看图:
上面这幅图的公式变化有没有没看明白的,没看明白的咱们评论区见哈。
最终我们得到了最重要的第5个公式的变形,即红色箭头后面的:
mod n m。
其中有几个关系,需要搞明白,m 与 n 互为质数,φn = x,d 是e相对于x的模反元素。
有没有看到一些加解密的雏形。
从 m 到 m。 这中间涵盖了从加密到解密的整个过程,但是缺少了我们想要的密文整个过程。
OK,下面引入本文的第四个数学公式:
我们来看一下整个交换流程:
1、客户端有一个数字13,服务端有一个数字15;
2、客户端通过计算 3的13次方 对 17 取余,得到数字12; 将12发送给服务端;同时服务端通过计算3的15次方,对17取余,得到数字6,将6发送给客户端。至此,整个交换过程完成。
3、服务端收到数字12以后,继续计算,12的15次方 对 17取余,得到 数字10。
4、客户端收到数字 6以后,继续计算,6的13次方 对 17 取余,得到数字 10。
有没有发现双方,最终得到了相同的内容10。但是这个数字10从来没有在网络过程中出现过。
好,讲到这里,可能有些人已经恍然大悟,这就是加密过程了,但是也有人会产生疑问,为什么要取数字3 和 17 呢,这里还牵涉到另一个数学知识,原根的问题。即3是17的原根。看图
有没有发现规律,3的1~16次方,对17取余,得到的整数是从1~16。这时我们称3为17的原根。也就是说上面的计算过程中有一组原根的关系。这是最早的迪菲赫尔曼秘钥交换算法。
解决了为什么取3和17的问题后,下面继续来看最终的RSA是如何产生的:
还记得我们上面提到的欧拉定理吗,其中 m 与 n 互为质数,n为质数,d 是 e 相对于 φn的模反元素。
当迪菲赫尔曼密钥交换算法碰上欧拉定理会产生什么呢?
我们得到下面的推论:
好,到这里我们是不是已经看到了整个的加密和解密过程了。
其中 m 是明文;c 是密文; n 和 e 为公钥;d 和 n 为私钥 。
其中几组数字的关系一定要明确:
1、d是e 相对于 φn 的模反元素,φn = n-1,即 e * d mod n = 1.
2、m 小于 n,上面在讲迪菲赫尔曼密钥交换算法时,提到原根的问题,在RSA加密算法中,对m和n并没有原根条件的约束。只要满足m与n互为质数,n为质数,且m < n就可以了。
OK,上面就是RSA加密算法的原理了,经过上面几个数学公式的狂轰乱炸,是不是有点迷乱了,给大家一些时间理一下,后面会和大家一起来验证RSA算法以及RSA为什么安全。
2. RSA 加密算法主要公式 - 草稿
从一道计算题开始了解 RSA 非对称加密算法的主要公式。
下面是一道关于 RSA 计算的问题,比较简单,从这道题来学习和了解关于 RSA 非对称加密算法的相关知识,当然,具体关于 RSA 加密算法的知识不能仅限于以下问题,应该更全面的了解相关的知识。但是下面的问题已经把其中的重点算法的表现出来了。
下面是关于 RSA 的主要数学公式:
n = p * q
ø(n) = (p - 1) * (q - 1)
ed ≡ 1 mod ø(n)
c = m e mod n
m = c d mod n
其中两个 ** 是次方的意思
ed ≡ 1 mod ø(n)
3 * 7 ≡ 1 mod ø(n)
21 ≡ 1 mod ø(n)
ø(n) = 20
ø(n) = 2 * 10 = (p - 1) * (q - 1)
p, q = 3, 11
n = p * q = 3 * 11 = 3
c = m**e mod n = 6 ** 3 mod 33
6 ** 3
3 = 1 * (2 ** 0) + 1 * (2 ** 1)
t0 = 6 mod 33 = 6
t1 = 6 ** 2 mod 33 = 3
t0 * t1 mod n = 6 * 3 mod 33 = 18
因此 明文 6 的密文是 18
m = c ** d mod n = 18 ** 7 mod 33
t0 = 18 mod 33 = 18
t1 = 18 ** 2 mod 33 = 27
t2 = 27 ** 2 mod 33 = 3
t0 * t1 * t2 mod n = 18 * 27 * 3 mod 33 = 6
因此,密文 18 解密后是 6
3. 对于加密的总结(AES,RSA)
跟第三方联调的时候会碰到各种加密算法,所以总结一下。
AES不是将拿到的明文一次性加密,而是分组加密,就是先将明文切分成长度相等的块,每块大小128bit,再对每一小块进行加密。那么问题就来了,并不是所有的原始明文串能被等分成128bit,例如原串大小200bit,那么第二个块只有72bit,所以就需要对第二个块进行填充处理,让第二个块的大小达到128bit。常见的填充模式有
不进行填充,要求原始加密串大小必须是128bit的整数倍;
假设块大小8字节,如果这个块跟8字节还差n个字节,那么就在原始块填充n,直到满8字节。例:块{1,2,3},跟8字节差了5个字节,那么补全后的结果{1,2,3,5,5,5,5,5}后面是五个5,块{1,2,3,..7}跟8字节差了1个字节,那么补全后就是{1,2,3,...,7,1},就是补了一个1。
如果恰好8字节又选择了PKCS5Padding填充方式呢?块{1,2,3...8}填充后变成{1,2,3...8,8...8},原串后面被补了8个8,这样做的原因是方便解密,只需要看最后一位就能算出原块的大小是多少。
跟PKCS5Padding的填充方式一样,不同的是,PKCS5Padding只是对8字节的进行填充,PKCS7Padding可以对1~256字节大小的block进行填充。openssl里aes的默认填充方式就是PKCS7Padding
AES有多种加密模式,包括:ECB,CBC,CTR,OCF,CFB,最常见的还是ECB和CBC模式。
最简单的一种加密模式,每个块进行独立加密,块与块之间加密互不影响,这样就能并行,效率高。
虽然这样加密很简单,但是不安全,如果两个块的明文一模一样,那么加密出来的东西也一模一样。
openssl的相关函数:
CBC模式中引入了一个新的概念,初始向量iv。iv的作用就是为了防止同样的明文块被加密成同样的内容。原理是第一个明文块跟初始向量做异或后加密,第二个块跟第一个密文块做异或再加密,依次类推,避免了同样的块被加密成同样的内容。
openssl相关函数:
敲黑板!! 所以跟第三方对接的时候,如果对面说他们用aes加密,务必对他们发起灵魂三问:
签名的作用是让接受方验证你传过去的数据没有被篡改;加密的作用是保证数据不被窃取。
原理:你有一个需要被验签的原串A。
步骤一:选择hash算法将A进行hash得到hash_a;
步骤二:将hash_a进行加密,得到加密值encrypt_a;
步骤三:将原串A和加密的encrypt_a发给第三方,第三方进行验签。第三方先解密encrypt_a,得到一个hash值hash_a1,然后对原串A使用同样的hash算法进行hash,得到的即为加密前的hash_a,如果hash_a = hash_a1, 那么验签成功。
rsa使用私钥对信息加密来做签名,使用公钥解密去验签。
openssl相关函数:
注意:两个函数中的m,是原串hash后的值,type表示生成m的算法,例如NID_sha256表示使用sha256对原串进行的hash,返回1为签名成功或者验签成功,-1位为失败。
再次敲黑板!! 所以如果第三方说使用rsa验签,要让对方告知他们的hash算法。
首先明确,私钥加密不等于签名。加密的时候,使用使用公钥加密,第三方使用你的私钥进行解密。
openssl里公钥加密函数为RSA_public_encrypt,私钥解密函数为RSA_private_decrypt,具体的可以自己去查看下官方文档。
rsa也涉及到了填充方式,所以对接的时候也要问清楚
在使用公钥进行加密时,会发现每次加密出的结果都不一样,但使用私钥加密时,每次的结果都一样,网上查了一圈,说是因为填充方式的原因。
官方文档说明:
那么为什么一定要使用私钥做签名,公钥做加密,而不是公钥做签名,私钥做加密呢?
举个栗子:
4. 公钥密码系统及RSA公钥算法
公钥密码系统及RSA公钥算法
本文简单介绍了公开密钥密码系统的思想和特点,并具体介绍了RSA算法的理论基础,工作原理和具体实现过程,并通过一个简单例子说明了该算法是如何实现。在本文的最后,概括说明了RSA算法目前存在的一些缺点和解决方法。
关键词:公钥密码体制 , 公钥 ,私钥 ,RSA
§1引言
随着计算机联网的逐步实现,Internet前景越来越美好,全球经济发展正在进入信息经济时代,知识经济初见端倪。计算机信息的保密问题显得越来越重要,无论是个人信息通信还是电子商务发展,都迫切需要保证Internet网上信息传输的安全,需要保证信息安全。信息安全技术是一门综合学科,它涉及信息论、计算机科学和密码学等多方面知识,它的主要任务是研究计算机系统和通信网络内信息的保护方法以实现系统内信息的安全、保密、真实和完整。其中,信息安全的核心是密码技术。密码技术是集数学、计算机科学、电子与通信等诸多学科于一身的交叉学科。它不仅能够保证机密性信息的加密,而且能够实现数字签名、身份验证、系统安全等功能。是现代化发展的重要科学之一。本文将对公钥密码系统及该系统中目前最广泛流行的RSA算法做一些简单介绍。
§2公钥密码系统
要说明公钥密码系统,首先来了解一下不同的加密算法:目前的加密算法按密钥方式可分为单钥密码算法和公钥密码算法。
2.1.单钥密码
又称对称式密码,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码(称为对称密码)。因此,通信双方都必须获得这把钥匙,并保持钥匙的秘密。
单钥密码系统的安全性依赖于以下两个因素:第一,加密算法必须是足够强的,仅仅基于密文本身去解密信息在实践上是不可能的;第二,加密方法的安全性依赖于密钥的秘密性,而不是算法的秘密性,因此,我们没有必要确保算法的秘密性(事实上,现实中使用的很多单钥密码系统的算法都是公开的),但是我们一定要保证密钥的秘密性。
从单钥密码的这些特点我们容易看出它的主要问题有两点:第一,密钥量问题。在单钥密码系统中,每一对通信者就需要一对密钥,当用户增加时,必然会带来密钥量的成倍增长,因此在网络通信中,大量密钥的产生﹑存放和分配将是一个难以解决的问题。第二,密钥分发问题。单钥密码系统中,加密的安全性完全依赖于对密钥的保护,但是由于通信双方使用的是相同的密钥,人们又不得不相互交流密钥,所以为了保证安全,人们必须使用一些另外的安全信道来分发密钥,例如用专门的信使来传送密钥,这种做法的代价是相当大的,甚至可以说是非常不现实的,尤其在计算机网络环境下,人们使用网络传送加密的文件,却需要另外的安全信道来分发密钥,显而易见,这是非常不智是甚至是荒谬可笑的。
2.2公钥密码
正因为单钥密码系统存在如此难以解决的缺点,发展一种新的﹑更有效﹑更先进的密码体制显得更为迫切和必要。在这种情况下,出现了一种新的公钥密码体制,它突破性地解决了困扰着无数科学家的密钥分发问题,事实上,在这种体制中,人们甚至不用分发需要严格保密的密钥,这次突破同时也被认为是密码史上两千年来自单码替代密码发明以后最伟大的成就。
这一全新的思想是本世纪70年代,美国斯坦福大学的两名学者Diffie和Hellman提出的,该体制与单钥密码最大的不同是:
在公钥密码系统中,加密和解密使用的是不同的密钥(相对于对称密钥,人们把它叫做非对称密钥),这两个密钥之间存在着相互依存关系:即用其中任一个密钥加密的信息只能用另一个密钥进行解密。这使得通信双方无需事先交换密钥就可进行保密通信。其中加密密钥和算法是对外公开的,人人都可以通过这个密钥加密文件然后发给收信者,这个加密密钥又称为公钥;而收信者收到加密文件后,它可以使用他的解密密钥解密,这个密钥是由他自己私人掌管的,并不需要分发,因此又成称为私钥,这就解决了密钥分发的问题。
为了说明这一思想,我们可以考虑如下的类比:
两个在不安全信道中通信的人,假设为Alice(收信者)和Bob(发信者),他们希望能够安全的通信而不被他们的敌手Oscar破坏。Alice想到了一种办法,她使用了一种锁(相当于公钥),这种锁任何人只要轻轻一按就可以锁上,但是只有Alice的钥匙(相当于私钥)才能够打开。然后Alice对外发送无数把这样的锁,任何人比如Bob想给她寄信时,只需找到一个箱子,然后用一把Alice的锁将其锁上再寄给Alice,这时候任何人(包括Bob自己)除了拥有钥匙的Alice,都不能再打开箱子,这样即使Oscar能找到Alice的锁,即使Oscar能在通信过程中截获这个箱子,没有Alice的钥匙他也不可能打开箱子,而Alice的钥匙并不需要分发,这样Oscar也就无法得到这把“私人密钥”。
从以上的介绍可以看出,公钥密码体制的思想并不复杂,而实现它的关键问题是如何确定公钥和私钥及加/解密的算法,也就是说如何找到“Alice的锁和钥匙”的问题。我们假设在这种体制中, PK是公开信息,用作加密密钥,而SK需要由用户自己保密,用作解密密钥。加密算法E和解密算法D也都是公开的。虽然SK与PK是成对出现,但却不能根据PK计算出SK。它们须满足条件:
①加密密钥PK对明文X加密后,再用解密密钥SK解密,即可恢复出明文,或写为:DSK(EPK(X))=X
②加密密钥不能用来解密,即DPK(EPK(X))≠X
③在计算机上可以容易地产生成对的PK和SK。
④从已知的PK实际上不可能推导出SK。
⑤加密和解密的运算可以对调,即:EPK(DSK(X))=X
从上述条件可看出,公开密钥密码体制下,加密密钥不等于解密密钥。加密密钥可对外公开,使任何用户都可将传送给此用户的信息用公开密钥加密发送,而该用户唯一保存的私人密钥是保密的,也只有它能将密文复原、解密。虽然解密密钥理论上可由加密密钥推算出来,但这种算法设计在实际上是不可能的,或者虽然能够推算出,但要花费很长的时间而成为不可行的。所以将加密密钥公开也不会危害密钥的安全。
这种体制思想是简单的,但是,如何找到一个适合的算法来实现这个系统却是一个真正困扰密码学家们的难题,因为既然Pk和SK是一对存在着相互关系的密钥,那么从其中一个推导出另一个就是很有可能的,如果敌手Oscar能够从PK推导出SK,那么这个系统就不再安全了。因此如何找到一个合适的算法生成合适的Pk和SK,并且使得从PK不可能推导出SK,正是迫切需要密码学家们解决的一道难题。这个难题甚至使得公钥密码系统的发展停滞了很长一段时间。
为了解决这个问题,密码学家们考虑了数学上的陷门单向函数,下面,我们可以给出它的非正式定义:
Alice的公开加密函数应该是容易计算的,而计算其逆函数(即解密函数)应该是困难的(对于除Alice以外的人)。许多形式为Y=f(x)的函数,对于给定的自变量x值,很容易计算出函数Y的值;而由给定的Y值,在很多情况下依照函数关系f (x)计算x值十分困难。这样容易计算但难于求逆的函数,通常称为单向函数。在加密过程中,我们希望加密函数E为一个单项的单射函数,以便可以解密。虽然目前还没有一个函数能被证明是单向的,但是有很多单射函数被认为是单向的。
例如,有如下一个函数被认为是单向的,假定n为两个大素数p和q的乘积,b为一个正整数,那么定义f:
f (x )= x b mod n
(如果gcd(b,φ(n))=1,那么事实上这就是我们以下要说的RSA加密函数)
如果我们要构造一个公钥密码体制,仅给出一个单向的单射函数是不够的。从Alice的观点来看,并不需要E是单向的,因为它需要用有效的方式解密所收到的信息。因此,Alice应该拥有一个陷门,其中包含容易求出E的你函数的秘密信息。也就是说,Alice可以有效解密,因为它有额外的秘密知识,即SK,能够提供给你解密函数D。因此,我们称一个函数为一个陷门单向函数,如果它是一个单向函数,并在具有特定陷门的知识后容易求出其逆。
考虑上面的函数f (x) = xb mod n。我们能够知道其逆函数f -1有类似的形式f (x ) = xa mod n,对于合适的取值a。陷门就是利用n的因子分解,有效的算出正确的指数a(对于给定的b)。
为方便起见,我们把特定的某类陷门单向函数计为?。那么随机选取一个函数f属于?,作为公开加密函数;其逆函数f-1是秘密解密函数。那么公钥密码体制就能够实现了。
根据以上关于陷门单向函数的思想,学者们提出了许多种公钥加密的方法,它们的安全性都是基于复杂的数学难题。根据所基于的数学难题,至少有以下三类系统目前被认为是安全和有效的:大整数因子分解系统(代表性的有RSA)、椭园曲线离散对数系统(ECC)和离散对数系统(代表性的有DSA)。
§3 RSA算法
3.1简介
当前最着名、应用最广泛的公钥系统RSA是在1978年,由美国麻省理工学院(MIT)的Rivest、Shamir和Adleman在题为《获得数字签名和公开钥密码系统的方法》的论文中提出的。它是一个基于数论的非对称(公开钥)密码体制,是一种分组密码体制。其名称来自于三个发明者的姓名首字母。它的安全性是基于大整数素因子分解的困难性,而大整数因子分解问题是数学上的着名难题,至今没有有效的方法予以解决,因此可以确保RSA算法的安全性。RSA系统是公钥系统的最具有典型意义的方法,大多数使用公钥密码进行加密和数字签名的产品和标准使用的都是RSA算法。
RSA算法是第一个既能用于数据加密也能用于数字签名的算法,因此它为公用网络上信息的加密和鉴别提供了一种基本的方法。它通常是先生成一对RSA密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册,人们用公钥加密文件发送给个人,个人就可以用私钥解密接受。为提高保密强度,RSA密钥至少为500位长,一般推荐使用1024位。
该算法基于下面的两个事实,这些事实保证了RSA算法的安全有效性:
1)已有确定一个数是不是质数的快速算法;
2)尚未找到确定一个合数的质因子的快速算法。
3.2工作原理
1)任意选取两个不同的大质数p和q,计算乘积r=p*q;
2)任意选取一个大整数e,e与(p-1)*(q-1)互质,整数e用做加密密钥。注意:e的选取是很容易的,例如,所有大于p和q的质数都可用。
3)确定解密密钥d:d * e = 1 molo(p - 1)*(q - 1) 根据e、p和q可以容易地计算出d。
4)公开整数r和e,但是不公开d;
5)将明文P (假设P是一个小于r的整数)加密为密文C,计算方法为:
C = Pe molo r
6)将密文C解密为明文P,计算方法为:
P = Cd molo r
然而只根据r和e(不是p和q)要计算出d是不可能的。因此,任何人都可对明文进行加密,但只有授权用户(知道d)才可对密文解密。
3.3简单实例
为了说明该算法的工作过程,我们下面给出一个简单例子,显然我们在这只能取很小的数字,但是如上所述,为了保证安全,在实际应用上我们所用的数字要大的多得多。
例:选取p=3, q=5,则r=15,(p-1)*(q-1)=8。选取e=11(大于p和q的质数),通过d * 11 = 1 molo 8,计算出d =3。
假定明文为整数13。则密文C为
C = Pe molo r
= 1311 molo 15
= 1,792,160,394,037 molo 15
= 7
复原明文P为:
P = Cd molo r
= 73 molo 15
= 343 molo 15
= 13
因为e和d互逆,公开密钥加密方法也允许采用这样的方式对加密信息进行"签名",以便接收方能确定签名不是伪造的。
假设A和B希望通过公开密钥加密方法进行数据传输,A和B分别公开加密算法和相应的密钥,但不公开解密算法和相应的密钥。A和B的加密算法分别是ECA和ECB,解密算法分别是DCA和DCB,ECA和DCA互逆,ECB和DCB互逆。 若A要向B发送明文P,不是简单地发送ECB(P),而是先对P施以其解密算法DCA,再用加密算法ECB对结果加密后发送出去。
密文C为:
C = ECB(DCA(P))
B收到C后,先后施以其解密算法DCB和加密算法ECA,得到明文P:
ECA(DCB(C))
= ECA(DCB(ECB(DCA(P))))
= ECA(DCA(P))/*DCB和ECB相互抵消*/
=
P /*DCB和ECB相互抵消*/
这样B就确定报文确实是从A发出的,因为只有当加密过程利用了DCA算法,用ECA才能获得P,只有A才知道DCA算法,没 有人,即使是B也不能伪造A的签名。
3.4优缺点
3.4.1优点
RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。该算法的加密密钥和加密算法分开,使得密钥分配更为方便。它特别符合计算机网络环境。对于网上的大量用户,可以将加密密钥用电话簿的方式印出。如果某用户想与另一用户进行保密通信,只需从公钥簿上查出对方的加密密钥,用它对所传送的信息加密发出即可。对方收到信息后,用仅为自己所知的解密密钥将信息脱密,了解报文的内容。由此可看出,RSA算法解决了大量网络用户密钥管理的难题,这是公钥密码系统相对于对称密码系统最突出的优点。
3.4.2缺点
1)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。
2)安全性, RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价,而且密码学界多数人士倾向于因子分解不是NPC问题。目前,人们已能分解140多个十进制位的大素数,这就要求使用更长的密钥,速度更慢;另外,目前人们正在积极寻找攻击RSA的方法,如选择密文攻击,一般攻击者是将某一信息作一下伪装(Blind),让拥有私钥的实体签署。然后,经过计算就可得到它所想要的信息。实际上,攻击利用的都是同一个弱点,即存在这样一个事实:乘幂保留了输入的乘法结构:
( XM )d = Xd *Md mod n
前面已经提到,这个固有的问题来自于公钥密码系统的最有用的特征--每个人都能使用公钥。但从算法上无法解决这一问题,主要措施有两条:一条是采用好的公钥协议,保证工作过程中实体不对其他实体任意产生的信息解密,不对自己一无所知的信息签名;另一条是决不对陌生人送来的随机文档签名,签名时首先使用One-Way Hash Function对文档作HASH处理,或同时使用不同的签名算法。除了利用公共模数,人们还尝试一些利用解密指数或φ(n)等等攻击.
3)速度太慢,由于RSA的分组长度太大,为保证安全性,n至少也要600 bitx以上,使运算代价很高,尤其是速度较慢,较对称密码算法慢几个数量级;且随着大数分解技术的发展,这个长度还在增加,不利于数据格式的标准化。目前,SET(Secure Electronic Transaction)协议中要求CA采用2048比特长的密钥,其他实体使用1024比特的密钥。为了速度问题,目前人们广泛使用单,公钥密码结合使用的方法,优缺点互补:单钥密码加密速度快,人们用它来加密较长的文件,然后用RSA来给文件密钥加密,极好的解决了单钥密码的密钥分发问题。
§4结束语
目前,日益激增的电子商务和其它因特网应用需求使公钥体系得以普及,这些需求量主要包括对服务器资源的访问控制和对电子商务交易的保护,以及权利保护、个人隐私、无线交易和内容完整性(如保证新闻报道或股票行情的真实性)等方面。公钥技术发展到今天,在市场上明显的发展趋势就是PKI与操作系统的集成,PKI是“Public
Key Infrastructure”的缩写,意为“公钥基础设施”。公钥体制广泛地用于CA认证、数字签名和密钥交换等领域。
公钥加密算法中使用最广的是RSA。RSA算法研制的最初理念与目标是努力使互联网安全可靠,旨在解决DES算法秘密密钥的利用公开信道传输分发的难题。而实际结果不但很好地解决了这个难题;还可利用RSA来完成对电文的数字签名以抗对电文的否认与抵赖;同时还可以利用数字签名较容易地发现攻击者对电文的非法篡改,以保护数据信息的完整性。目前为止,很多种加密技术采用了RSA算法,该算法也已经在互联网的许多方面得以广泛应用,包括在安全接口层(SSL)标准(该标准是网络浏览器建立安全的互联网连接时必须用到的)方面的应用。此外,RSA加密系统还可应用于智能IC卡和网络安全产品。
但目前RSA算法的专利期限即将结束,取而代之的是基于椭圆曲线的密码方案(ECC算法)。较之于RSA算法,ECC有其相对优点,这使得ECC的特性更适合当今电子商务需要快速反应的发展潮流。此外,一种全新的量子密码也正在发展中。
至于在实际应用中应该采用何种加密算法则要结合具体应用环境和系统,不能简单地根据其加密强度来做出判断。因为除了加密算法本身之外,密钥合理分配、加密效率与现有系统的结合性以及投入产出分析都应在实际环境中具体考虑。加密技术随着网络的发展更新,将有更安全更易于实现的算法不断产生,为信息安全提供更有力的保障。今后,加密技术会何去何从,我们将拭目以待。
参考文献:
[1] Douglas R.Stinson.《密码学原理与实践》.北京:电子工业出版社,2003,2:131-132
[2]西蒙.辛格.《密码故事》.海口:海南出版社,2001,1:271-272
[3]嬴政天下.加密算法之RSA算法.http://soft.winzheng.com/infoView/Article_296.htm,2003
[4]加密与数字签名.http://www.njt.cn/yumdq/dzsw/a2.htm
[5]黑客中级教程系列之十.http://www.qqorg.i-p.com/jiaocheng/10.html
5. RSA PKCS#1在java中怎么实现
楼主看看下面的代码是不是你所需要的,这是我原来用的时候收集的
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.io.*;
import java.math.BigInteger;
/**
* RSA 工具类。提供加密,解密,生成密钥对等方法。
* 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
* RSA加密原理概述
* RSA的安全性依赖于大数的分解,公钥和私钥都是两个大素数(大于100的十进制位)的函数。
* 据猜测,从一个密钥和密文推断出明文的难度等同于分解两个大素数的积
* ===================================================================
* (该算法的安全性未得到理论的证明)
* ===================================================================
* 密钥的产生:
* 1.选择两个大素数 p,q ,计算 n=p*q;
* 2.随机选择加密密钥 e ,要求 e 和 (p-1)*(q-1)互质
* 3.利用 Euclid 算法计算解密密钥 d , 使其满足 e*d = 1(mod(p-1)*(q-1)) (其中 n,d 也要互质)
* 4:至此得出公钥为 (n,e) 私钥为 (n,d)
* ===================================================================
* 加解密方法:
* 1.首先将要加密的信息 m(二进制表示) 分成等长的数据块 m1,m2,...,mi 块长 s(尽可能大) ,其中 2^s<n
* 2:对应的密文是: ci = mi^e(mod n)
* 3:解密时作如下计算: mi = ci^d(mod n)
* ===================================================================
* RSA速度
* 由于进行的都是大数计算,使得RSA最快的情况也比DES慢上100倍,无论是软件还是硬件实现。
* 速度一直是RSA的缺陷。一般来说只用于少量数据加密。
* 文件名:RSAUtil.java<br>
* @author 赵峰<br>
* 版本:1.0.1<br>
* 描述:本算法摘自网络,是对RSA算法的实现<br>
* 创建时间:2009-7-10 下午09:58:16<br>
* 文件描述:首先生成两个大素数,然后根据Euclid算法生成解密密钥<br>
*/
public class RSAUtil {
//密钥对
private KeyPair keyPair = null;
/**
* 初始化密钥对
*/
public RSAUtil(){
try {
this.keyPair = this.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成密钥对
* @return KeyPair
* @throws Exception
*/
private KeyPair generateKeyPair() throws Exception {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
//这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
final int KEY_SIZE = 1024;
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.genKeyPair();
return keyPair;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 生成公钥
* @param molus
* @param publicExponent
* @return RSAPublicKey
* @throws Exception
*/
private RSAPublicKey generateRSAPublicKey(byte[] molus, byte[] publicExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(molus), new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 生成私钥
* @param molus
* @param privateExponent
* @return RSAPrivateKey
* @throws Exception
*/
private RSAPrivateKey generateRSAPrivateKey(byte[] molus, byte[] privateExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(molus), new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 加密
* @param key 加密的密钥
* @param data 待加密的明文数据
* @return 加密后的数据
* @throws Exception
*/
public byte[] encrypt(Key key, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, key);
// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024 加密块大小为127 byte,加密后为128个byte;
// 因此共有2个加密块,第一个127 byte第二个为1个byte
int blockSize = cipher.getBlockSize();
// System.out.println("blockSize:"+blockSize);
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
// System.out.println("加密块大小:"+outputSize);
int leavedSize = data.length % blockSize;
// System.out.println("leavedSize:"+leavedSize);
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize)
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中
// 而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 解密
* @param key 解密的密钥
* @param raw 已经加密的数据
* @return 解密后的明文
* @throws Exception
*/
@SuppressWarnings("static-access")
public byte[] decrypt(Key key, byte[] raw) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, key);
int blockSize = cipher.getBlockSize();
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 返回公钥
* @return
* @throws Exception
*/
public RSAPublicKey getRSAPublicKey() throws Exception{
//获取公钥
RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
//获取公钥系数(字节数组形式)
byte[] pubModBytes = pubKey.getMolus().toByteArray();
//返回公钥公用指数(字节数组形式)
byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray();
//生成公钥
RSAPublicKey recoveryPubKey = this.generateRSAPublicKey(pubModBytes,pubPubExpBytes);
return recoveryPubKey;
}
/**
* 获取私钥
* @return
* @throws Exception
*/
public RSAPrivateKey getRSAPrivateKey() throws Exception{
// 获取私钥
RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();
// 返回私钥系数(字节数组形式)
byte[] priModBytes = priKey.getMolus().toByteArray();
// 返回私钥专用指数(字节数组形式)
byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray();
// 生成私钥
RSAPrivateKey recoveryPriKey = this.generateRSAPrivateKey(priModBytes,priPriExpBytes);
return recoveryPriKey;
}
/**
* 测试
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RSAUtil rsa = new RSAUtil();
String str = "天龙八部、神雕侠侣、射雕英雄传白马啸西风";
RSAPublicKey pubKey = rsa.getRSAPublicKey();
RSAPrivateKey priKey = rsa.getRSAPrivateKey();
// System.out.println("加密后==" + new String(rsa.encrypt(pubKey,str.getBytes())));
String mw = new String(rsa.encrypt(pubKey, str.getBytes()));
System.out.println("加密后:"+mw);
// System.out.println("解密后:");
System.out.println("解密后==" + new String(rsa.decrypt(priKey,rsa.encrypt(pubKey,str.getBytes()))));
}
}
6. RSA中pkcs1的填充方法具体是什么
1)RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入 必须 比 RSA 钥模长(molus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充
输出 和molus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2) RSA_PKCS1_OAEP_PADDING
RSA_size(rsa) – 41
3)for RSA_NO_PADDING 不填充
RSA_size(rsa)
一般来说, 我们只用RSA来加密重要的数据,比如AES的key, 128bits = 16
加密的输出,总是等于key length
对同样的数据,用同样的key进行RSA加密, 每次的输出都会不一样; 但是这些加密的结果都能正确的解密
—————
预备知识
I2OSP – Integer-to-Octet-String primitive 大整数转换成字节串
I2OSP (x, xLen)
输入: x 待转换的非负整数
xLen 结果字节串的可能长度
————
加密原理 RSAEP ((n, e), m)
输入: (n,e) RSA公钥
m 值为0到n-1 之间一个大整数,代表消息
输出: c 值为0到n-1之间的一个大整数,代表密文
假定: RSA公钥(n,e)是有效的
步骤:
1. 如果m不满足 0 2. 让 c = m^e % n (m的e次幂 除以n ,余数为c)
3. 输出 c
解密原理 RSADP (K, c)
输入: K RSA私钥,K由下面形式:
一对(n,d)
一个五元组(p, q, dP, dQ, qInv)
一个可能为空的三元序列(ri, di, ti), i=3,...,u
c 密文
输出: m 明文
步骤:
1. 如果密文c不满足 0 < c < n-1, 输出 'ciphertext repersentative out of range'
2. 按照如下方法计算m:
a. 如果使用私钥K的第一种形式(n, d), 就让 m = c^d % n (c的d次幂,除以n,余数为m)
b. 如果使用私钥K的第二种像是(p,q, dP, dQ, qInv)和(ri, di, ti),
--------------
----------------
加密 RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
输入: (n, e) 接收者的公开钥匙, k表示n所占用的字节长度
M 要加密的消息, mLen表示消息的长度 mLen ≤ k – 11
输出: C 密文, 占用字节数 也为 k
步骤:
1.长度检查, 如果 mLen > k-11, 输出 “message too long”
2. EME-PKCS1-v1_5 编码
a) 生成一个 伪随机非零串PS , 长度为 k – mLen – 3, 所以至少为8, 因为 k-mLen>11
b) 将PS, M,以及其他填充串 一起编码为 EM, 长度为 k, 即:
EM = 0×00 || 0×02 || PS || 0×00 || M
3.RSA 加密
a)将EM转换成一个大证书m
m = OS2IP(EM)
b)对公钥(n,e) 和 大整数 m, 使用RSAEP加密原理,产生一个整数密文c
c = RSAEP((n,e0, m)
c)将整数c转换成长度为k的密文串
C = I2OSP(c, k)
4.输出密文C
—————-
解密 RSAES-PKCS1-V1_5-DECRYPT (K, C)
输入: K 接收者的私钥
C 已经加密过的密文串,长度为k (与RSA molus n的长度一样)
输出: M 消息明文, 长度至多为 k-11
步骤:
1. 长度检查:如果密文C的长度不为k字节(或者 如果 k<11), 输出“decryption error"
2. RSA解密
a. 转换密文C为一个大整数c
c = OS2IP(C)
b. 对RSA私钥(n,d)和密文整数c 实施解密, 产生一个 大整数m
m = RSADP((n,d), c)
如果RSADP输出'ciphertext representative out of range'(意味c>=n), 就输出’decryption error”
c. 转换 m 为长度为k的EM串
EM = I2OSP(m, k)
3. EME-PKCS1-v1_5 解码:将EM分为 非零的PS串 和 消息 M
EM = 0×00 || 0×02 || PS || 0×00 || M
如果EM不是上面给出的格式,或者PS的长度小于8个字节, 那么就输出’decryption error’
5. 输出明文消息M
——————–
签名 RSASSA-PSS-SIGN (K, M)
输入 K 签名者的RSA私钥
M 代签名的消息,一个字节串
输出 S 签名,长度为k的字节串,k是RSA molus n的字节长度
步骤:
1. EMSA-PSS encoding: 对消息M实施EMSA-PSS编码操作,产生一个长度为 [(modBits -1)/8]的编码消息EM。 整数 OS2IP(EM)的位长最多是 modBits-1, modBits是RSA molus n的位长度
EM = EMSA-PSS-ENCODE (M, modBits – 1)
注意:如果modBits-1 能被8整除,EM的字节长比k小1;否则EM字节长为k
2. RSA签名:
a. 将编码后的消息 EM 转换成一个大整数m
m = OS2IP(EM)
b. 对私钥K和消息m 实施 RSASP1 签名,产生一个 大整数s表示的签名
s = RSASP1 (K, m)
c. 把大整数s转换成 长度为k的字串签名S
S = I2OSP(s, k)
3.输出签名S
———–
验证签名 RSASSA-PSS-VERIFY ((n, e), M, S)
输入: (n,e) 签名者的公钥
M 签名者 发来的消息,一个字串
S 待验证的签名, 一个长度为k的字串。k是RSA Molus n的长度
输出: ’valid signature’ 或者 ‘invalid signature’
步骤:
1. 长度检查: 如果签名S的长度不是k, 输出’invalid signature’
2. RSA验证
a) 将签名S转换成一个大整数s
s = OS2IP (S)
b) 对公钥 (n,e) 和 s 实施 RSAVP1 验证, 产生一个 大整数m
m = RSAVP1 ((n, e), s)
c) 将m 转换成编码的消息EM,长度 emLen = [ (modBits -1)/8 ] 字节。 modBits是RSA molus n的位长
EM = I2OSP (m, emLen)
注意: 如果 modBits-1可以被8整除,那么emLen = k-1,否则 emLen = k
3. EMSA-PSS验证: 对消息M和编码的EM实施一个 EMSA-PSS验证操作,决定他们是否一致:
Result = EMSA-PSS-VERIFY (M, EM, modBits – 1)
4. 如果Result = “consistent“,那么输出 ”valid signature”
否则, 输出 ”invalid signature”
———–
签名,还可以使用 EMSA-PKCS1-v1_5 encoding编码方法 来产生 EM:
EM = EMSA-PKCS1-V1_5-ENCODE (M, k)
验证签名是,使用 EMSA-PKCS1-v1_5对 M产生第2个编码消息EM’
EM’ = EMSA-PKCS1-V1_5-ENCODE (M, k) .
然后比较 EM和EM’ 是否相同
———————
RSA的加密机制有两种方案一个是RSAES-OAEP,另一个RSAES-PKCS1-v1_5。PKCS#1推荐在新的应用中使用RSAES- OAEP,保留RSAES-PKCS#1-v1_5跟老的应用兼容。它们两的区别仅仅在于加密前编码的方式不同。而加密前的编码是为了提供了抵抗各种活动的敌对攻击的安全机制。
PKCS#1的签名机制也有种方案:RSASSA-PSS和RSASSA-PKCS1-v1_5。同样,推荐RSASSA-PSS用于新的应用而RSASSA-PKCS1-v1_5用于兼容老的应用。
——————–
RSAES-OAEP-ENCRYPT ((n, e), M, L)
选项: Hash 散列函数(hLen 表示 散列函数的输出的字节串的长度)
MGF 掩码生成函数
输入: (n,e) 接收者的RSA公钥(k表示RSA molus n的字节长度)
M 待加密的消息,一个长度为mLen的字节串 mLen <= k - 2 hLen -2
L 同消息关联的可选的标签,如果不提供L,就采用空串
输出: C 密文,字节长度为k
步骤:
1.长度检查
a. 如果L的长度超过 hash函数的输入限制(对于SHA-1, 是2^61 -1),输出 label too long
b. mLen > k – 2hLen -2, 输出 message too long
2. EME-OAEP编码
说实话,我看了很久不太懂。。。。。。。
7. RSA加解密原理以及三种填充模式
如果需要理解RSA的加密原理,需要理解以下理论:
等同于求一元二次方程 23 * d + 192 * y = 1
可以求得其中一解为(d=167,y=-20)
至此就完成了所有的计算
对于上述例子的到公钥(221,23)和私钥(221,167)
在上述的计算过程中一共用到了
上面用到的数中只有公钥部分是公开的,即(221,23)。那么我们是否可以通过公钥来推到出私钥部分,即已知n和e,推到出d?
(1)ed 1(mod (n)),只有知道 (n)才能解出d
(2) (n)= (p) (q)= (p-1) (q-1),只有知道p和q才能得到 (n)
(3)n=p q,就需要对n进行因式分解
那么如果可以对n因式分解就可以求出d,也就意味着私匙被破解
那么RSA加密的可靠性就在于对n因式分解的难度,而现在对一个整数n做因式分解并没有巧妙的算法,只有通过暴力破解计算。在实际应用中的n取值通常在1024位以上,而公开已知的可因式分解的最大数为768位。所以现阶段来说RSA加密是可靠的。
现在我们就可以进行加密和解密了
我们使用上面生成的公钥(221,23)来加密。如果我们需要加密的信息是m( m必须为整数并且m要小于n ),m取56,可以用以下公式求出加密串c:
c (mod n)
10 (mod 221)
可以求出加密后的结果c为10
密钥为(221,167),加密结果c=10,可以使用以下公式求出被加密的信息
m (mod n) 即加密结果的d次方除以n的余数为m
56 (mod 221)
RSA加密属于块加密算法,总是在一个固定长度的块上进行操作。如果被加密的字符串过长,则需要对字符串进行切割,如果字符串过短则需要进行填充。
以下主介绍一下RSA_PKCS1_PADDING填充模式以及RSA_NO_PADDING模式
此填充模式是最常用的填充模式,在此填充模式下输入的长度受加密钥的长度限制,输入的最大长度为加密钥的位数k-11。如果公钥的长度为1024位即128字节,那么输入的长度最多为128-11=117字节。如果长度小于117就需要填充。如果输入T的长度为55字节,填充后的块为EM,则EM格式如下:
EM= 0x00 || BT || PS || 0x00 || T
在此填充模式下,输入的长度最多和RSA公钥长度一样长,如果小于公钥长度则会在前面填充0x00。如果公钥长度是128字节,输入T的长度为55字节,填充后的块为EM,则EM格式如下:
EM=P || T
参考:
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
https://my.oschina.net/3pgp/blog/749195
8. 公钥密码→RSA详解
在对称密码中,由于加密和解密的密钥是相同的,因此必须向接收者配送密钥。用于解密的密钥必须被配送给接收者,这一问题称为 密钥配送问题 ,如果使用公钥密码,则无需向接收者配送用于解密的密钥,这样就解决了密钥配送问题。可以说公钥密码是密码学历史上最伟大的发明。
解决密钥配送问题的方法
在人数很多的情况下,通信所需要的密钥数量会增大,例如:1000名员工中每一个人都可以和另外999个进行通信,则每个人需要999个通信密钥,整个密钥数量:
1000 x 999 ÷ 2 = 499500
很不现实,因此此方法有一定的局限性
在Diffic-Hellman密钥交换中,进行加密通信的双方需要交换一些信息,而这些信息即便被窃听者窃听到也没有问题(后续文章会进行详解)。
在对称密码中,加密密钥和解密密钥是相同的,但公钥密码中,加密密钥和解密密钥却是不同的。只要拥有加密密钥,任何人都可以加密,但没有解密密钥是无法解密的。
公钥密码中,密钥分为加密密钥(公钥)和解密密钥(私钥)两种。
公钥和私钥是一一对应的,一对公钥和私钥统称为密钥对,由公钥进行加密的密文,必须使用与该公钥配对的私钥才能够解密。密钥对中的两个密钥之间具有非常密切的关系——数学上的关系——因此公钥和私钥是不能分别单独生成的。
发送者:Alice 接收者:Bob 窃听者:Eve
通信过程是由接收者Bob来启动的
公钥密码解决了密钥配送的问题,但依然面临着下面的问题
RSA是目前使用最广泛的公钥密码算法,名字是由它的三位开发者,即Ron Rivest、Adi Shamir和Leonard Adleman的姓氏的首字母组成的(Rivest-Shamir-Adleman)。RSA可以被使用公钥密码和数字签名(此文只针对公钥密码进行探讨,数字签名后续文章敬请期待)1983年在美国取得了专利,但现在该专利已经过期。
在RSA中,明文、密钥和密文都是数字,RSA加密过程可以用下列公式来表达
密文 = 明文 E mod N
简单的来说,RSA的密文是对代表明文的数字的 E 次方求mod N 的结果,换句话说:将明文和自己做 E 次乘法,然后将结果除以 N 求余数,这个余数就是密文。
RSA解密过程可以用下列公式来表达
明文 = 密文 D mod N
对表示密文的数字的 D 次方求mod N 就可以得到明文,换句话说:将密文和自己做 D 次乘法,在对其结果除以 N 求余数,就可以得到明文
此时使用的数字 N 和加密时使用的数字 N 是相同的,数 D 和数 N 组合起来就是RSA的解密密钥,因此 D 和 N 的组合就是私钥 。只要知道 D 和 N 两个数的人才能够完成解密的运算
根据加密和解密的公式可以看出,需要用到三个数—— E 、 D 和 N 求这三个数就是 生成密钥对 ,RSA密钥对的生成步骤如下:
准备两个很大的质数 p 和 q ,将这两个数相乘,结果就是 N
N = p x q
L 是 p-1 和 q-1 的最小公倍数,如果用lcm( X , Y )来表示 “ X 和 Y 的最小公倍数” 则L可以写成下列形式
L = lcm ( p - 1, q - 1)
E 是一个比1大、比 L 小的数。 E 和 L 的最大公约数必须为1,如果用gcd( X , Y )来表示 X 和 Y 的最大公约数,则 E 和 L 之间存在下列关系:
1 < E < L
gcd( E , L ) = 1 (是为了保证一定存在解密时需要使用的数 D )
1 < D < L
E x D mod L = 1
p = 17
q = 19
N = p x q = 17 x 19 = 323
L = lcm ( p - 1, q - 1) = lcm (16,18) = 144
gcd( E , L ) = 1
满足条件的 E 有很多:5,7,11,13,17,19,23,25,29,31...
这里选择5来作为 E ,到这里我们已经知道 E = 5 N = 323 这就是公钥
E x D mod L = 1
D = 29 可以满足上面的条件,因此:
公钥: E = 5 N = 323
私钥: D = 29 N = 323
要加密的明文必须是小于 N 的数,这是因为在加密运算中需要求 mod N 假设加密的明文是123
明文 E mod N = 123 5 mod 323 = 225(密文)
对密文225进行解密
密文 D mod N = 225 29 mod 323 = 225 10 x 225 10 x 225 9 mod 323 = (225 10 mod 323) x (225 10 mod 323) x (225 9 mod 323) = 16 x 16 x 191 mod 323 = 48896 mod 323 = 123(明文)
如果没有mod N 的话,即:
明文 = 密文 D mod N
通过密文求明文的难度不大,因为这可以看作是一个求对数的问题。
但是,加上mod N 之后,求明文就变成了求离散对数的问题,这是非常困难的,因为人类还没有发现求离散对数的高效算法。
只要知道 D ,就能够对密文进行解密,逐一尝试 D 来暴力破译RSA,暴力破解的难度会随着D的长度增加而加大,当 D 足够长时,就不能再现实的时间内通过暴力破解找出数 D
目前,RSA中所使用的 p 和 q 的长度都是1024比特以上, N 的长度为2048比特以上,由于 E 和 D 的长度可以和N差不多,因此要找出 D ,就需要进行2048比特以上的暴力破解。这样的长度下暴力破解找出 D 是极其困难的
E x D mod L = 1 L = lcm ( p - 1, q - 1)
由 E 计算 D 需要使用 p 和 q ,但是密码破译者并不知道 p 和 q
对于RSA来说,有一点非常重要,那就是 质数 p 和 q 不能被密码破译这知道 。把 p 和 q 交给密码破译者与把私钥交给密码破译者是等价的。
p 和 q 不能被密码破译者知道,但是 N = p x q 而且 N 是公开的, p 和 q 都是质数,因此由 N 求 p 和 q 只能通过 将 N 进行质因数分解 ,所以说:
一旦发现了对大整数进行质因数分解的高效算法,RSA就能够被破译
这种方法虽然不能破译RSA,但却是一种针对机密性的有效攻击。
所谓中间人攻击,就是主动攻击者Mallory混入发送者和接收者的中间,对发送者伪装成接收者,对接收者伪装成发送者的攻击,在这里,Mallory就是“中间人”
这种攻击不仅针对RSA,而是可以针对任何公钥密码。在这个过程中,公钥密码并没有被破译,所有的密码算法也都正常工作并确保了机密性。然而,所谓的机密性并非在Alice和Bob之间,而是在Alice和Mallory之间,以及Mallory和Bob之间成立的。 仅靠公钥密码本身,是无法防御中间人攻击的。
要防御中间人攻击,还需要一种手段来确认所收到的公钥是否真的属于Bob,这种手段称为认证。在这种情况下,我们可以使用公钥的 证书 (后面会陆续更新文章来进行探讨)
网络上很多服务器在收到格式不正确的数据时都会向通信对象返回错误消息,并提示“这里的数据有问题”,然而,这种看似很贴心的设计却会让攻击者有机可乘。 攻击者可以向服务器反复发送自己生成的伪造密文,然后分析返回的错误消息和响应时间获得一些关于密钥和明文的信息。
为了抵御这种攻击,可以对密文进行“认证”,RSA-OAEP(最优非对称加密填充)正是基于这种思路设计的一种RSA改良算法。
RSA-OAEP在加密时会在明文前面填充一些认证信息,包括明文的散列值以及一定数量的0,然后用RSA进行加密,在解密的过程中,如果解密后的数据的开头没有找到正确的认证信息,则可以判定有问题,并返回固定的错误消息(重点是,不能将具体的错误内容告知开发者)
RSA-OAEP在实际应用中,还会通过随机数使得每次生成的密文呈现不同的排列方式,从而进一步提高安全性。
随着计算机技术的进步等,以前被认为是安全的密码会被破译,这一现象称为 密码劣化 ,针对这一点:
9. 数据加密方式有哪些
对称加密:三重DES、AES、SM4等
非对称加密:RSA、SM2等
其他的保护数据隐私的方法还有同态加密、差分隐私、安全多方计算等
目前我们公司一直和上海安策信息合作的,安策信息研发了好几种数据加密工具,包括加密狗、加密机、动态口令、加密工具等网络也有很多相关资料。
10. 加密基础知识二 非对称加密RSA算法和对称加密
上述过程中,出现了公钥(3233,17)和私钥(3233,2753),这两组数字是怎么找出来的呢?参考 RSA算法原理(二)
首字母缩写说明:E是加密(Encryption)D是解密(Decryption)N是数字(Number)。
1.随机选择两个不相等的质数p和q。
alice选择了61和53。(实际应用中,这两个质数越大,就越难破解。)
2.计算p和q的乘积n。
n = 61×53 = 3233
n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。
3.计算n的欧拉函数φ(n)。称作L
根据公式φ(n) = (p-1)(q-1)
alice算出φ(3233)等于60×52,即3120。
4.随机选择一个整数e,也就是公钥当中用来加密的那个数字
条件是1< e < φ(n),且e与φ(n) 互质。
alice就在1到3120之间,随机选择了17。(实际应用中,常常选择65537。)
5.计算e对于φ(n)的模反元素d。也就是密钥当中用来解密的那个数字
所谓"模反元素"就是指有一个整数d,可以使得ed被φ(n)除的余数为1。ed ≡ 1 (mod φ(n))
alice找到了2753,即17*2753 mode 3120 = 1
6.将n和e封装成公钥,n和d封装成私钥。
在alice的例子中,n=3233,e=17,d=2753,所以公钥就是 (3233,17),私钥就是(3233, 2753)。
上述故事中,blob为了偷偷地传输移动位数6,使用了公钥做加密,即6^17 mode 3233 = 824。alice收到824之后,进行解密,即824^2753 mod 3233 = 6。也就是说,alice成功收到了blob使用的移动位数。
再来复习一下整个流程:
p=17,q=19
n = 17 19 = 323
L = 16 18 = 144
E = 5(E需要满足以下两个条件:1<E<144,E和144互质)
D = 29(D要满足两个条件,1<D<144,D mode 144 = 1)
假设某个需要传递123,则加密后:123^5 mode 323 = 225
接收者收到225后,进行解密,225^ 29 mode 323 = 123
回顾上面的密钥生成步骤,一共出现六个数字:
p
q
n
L即φ(n)
e
d
这六个数字之中,公钥用到了两个(n和e),其余四个数字都是不公开的。其中最关键的是d,因为n和d组成了私钥,一旦d泄漏,就等于私钥泄漏。那么,有无可能在已知n和e的情况下,推导出d?
(1)ed≡1 (mod φ(n))。只有知道e和φ(n),才能算出d。
(2)φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。
(3)n=pq。只有将n因数分解,才能算出p和q。
结论:如果n可以被因数分解,d就可以算出,也就意味着私钥被破解。
可是,大整数的因数分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。维基网络这样写道:"对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。"
然而,虽然RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价。即RSA的重大缺陷是无法从理论上把握它的保密性能如何。此外,RSA的缺点还有:
A)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。
B)分组长度太大,为保证安全性,n 至少也要 600bits以上,使运算代价很高,尤其是速度较慢,较对称密码算法慢几个数量级;且随着大数分解技术的发展,这个长度还在增加,不利于数据格式的标准化。因此, 使用RSA只能加密少量数据,大量的数据加密还要靠对称密码算法 。
加密和解密是自古就有技术了。经常看到侦探电影的桥段,勇敢又机智的主角,拿着一长串毫无意义的数字苦恼,忽然灵光一闪,翻出一本厚书,将第一个数字对应页码数,第二个数字对应行数,第三个数字对应那一行的某个词。数字变成了一串非常有意义的话:
Eat the beancurd with the peanut. Taste like the ham.
这种加密方法是将原来的某种信息按照某个规律打乱。某种打乱的方式就叫做密钥(cipher code)。发出信息的人根据密钥来给信息加密,而接收信息的人利用相同的密钥,来给信息解密。 就好像一个带锁的盒子。发送信息的人将信息放到盒子里,用钥匙锁上。而接受信息的人则用相同的钥匙打开。加密和解密用的是同一个密钥,这种加密称为对称加密(symmetric encryption)。
如果一对一的话,那么两人需要交换一个密钥。一对多的话,比如总部和多个特工的通信,依然可以使用同一套密钥。 但这种情况下,对手偷到一个密钥的话,就知道所有交流的信息了。 二战中盟军的情报战成果,很多都来自于破获这种对称加密的密钥。
为了更安全,总部需要给每个特工都设计一个不同的密钥。如果是FBI这样庞大的机构,恐怕很难维护这么多的密钥。在现代社会,每个人的信用卡信息都需要加密。一一设计密钥的话,银行怕是要跪了。
对称加密的薄弱之处在于给了太多人的钥匙。如果只给特工锁,而总部保有钥匙,那就容易了。特工将信息用锁锁到盒子里,谁也打不开,除非到总部用唯一的一把钥匙打开。只是这样的话,特工每次出门都要带上许多锁,太容易被识破身份了。总部老大想了想,干脆就把造锁的技术公开了。特工,或者任何其它人,可以就地取材,按照图纸造锁,但无法根据图纸造出钥匙。钥匙只有总部的那一把。
上面的关键是锁和钥匙工艺不同。知道了锁,并不能知道钥匙。这样,银行可以将“造锁”的方法公布给所有用户。 每个用户可以用锁来加密自己的信用卡信息。即使被别人窃听到,也不用担心:只有银行才有钥匙呢!这样一种加密算法叫做非对称加密(asymmetric encryption)。非对称加密的经典算法是RSA算法。它来自于数论与计算机计数的奇妙结合。
1976年,两位美国计算机学家Whitfield Diffie 和 Martin Hellman,提出了一种崭新构思,可以在不直接传递密钥的情况下,完成解密。这被称为"Diffie-Hellman密钥交换算法"。这个算法启发了其他科学家。人们认识到,加密和解密可以使用不同的规则,只要这两种规则之间存在某种对应关系即可,这样就避免了直接传递密钥。这种新的加密模式被称为"非对称加密算法"。
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。
1.能“撞”上的保险箱(非对称/公钥加密体制,Asymmetric / Public Key Encryption)
数据加密解密和门锁很像。最开始的时候,人们只想到了那种只能用钥匙“锁”数据的锁。如果在自己的电脑上自己加密数据,当然可以用最开始这种门锁的形式啦,方便快捷,简单易用有木有。
但是我们现在是通信时代啊,双方都想做安全的通信怎么办呢?如果也用这种方法,通信就好像互相发送密码保险箱一样…而且双方必须都有钥匙才能进行加密和解密。也就是说,两个人都拿着保险箱的钥匙,你把数据放进去,用钥匙锁上发给我。我用同样的钥匙把保险箱打开,再把我的数据锁进保险箱,发送给你。
这样看起来好像没什么问题。但是,这里面 最大的问题是:我们两个怎么弄到同一个保险箱的同一个钥匙呢? 好像仅有的办法就是我们两个一起去买个保险箱,然后一人拿一把钥匙,以后就用这个保险箱了。可是,现代通信社会,绝大多数情况下别说一起去买保险箱了,连见个面都难,这怎么办啊?
于是,人们想到了“撞门”的方法。我这有个可以“撞上”的保险箱,你那里自己也买一个这样的保险箱。通信最开始,我把保险箱打开,就这么开着把保险箱发给你。你把数据放进去以后,把保险箱“撞”上发给我。撞上以后,除了我以外,谁都打不开保险箱了。这就是RSA了,公开的保险箱就是公钥,但是我有私钥,我才能打开。
2.数字签名
这种锁看起来好像很不错,但是锁在运输的过程中有这么一个严重的问题:你怎么确定你收到的开着的保险箱就是我发来的呢?对于一个聪明人,他完全可以这么干:
(a)装作运输工人。我现在把我开着的保险箱运给对方。运输工人自己也弄这么一个保险箱,运输的时候把保险箱换成他做的。
(b)对方收到保险箱后,没法知道这个保险箱是我最初发过去的,还是运输工人替换的。对方把数据放进去,把保险箱撞上。
(c)运输工人往回运的时候,用自己的钥匙打开自己的保险箱,把数据拿走。然后复印也好,伪造也好,弄出一份数据,把这份数据放进我的保险箱,撞上,然后发给我。
从我的角度,从对方的角度,都会觉得这数据传输过程没问题。但是,运输工人成功拿到了数据,整个过程还是不安全的,大概的过程是这样:
这怎么办啊?这个问题的本质原因是,人们没办法获知,保险箱到底是“我”做的,还是运输工人做的。那干脆,我们都别做保险箱了,让权威机构做保险箱,然后在每个保险箱上用特殊的工具刻上一个编号。对方收到保险箱的时候,在权威机构的“公告栏”上查一下编号,要是和保险箱上的编号一样,我就知道这个保险箱是“我”的,就安心把数据放进去。大概过程是这样的:
如何做出刻上编号,而且编号没法修改的保险箱呢?这涉及到了公钥体制中的另一个问题:数字签名。
要知道,刻字这种事情吧,谁都能干,所以想做出只能自己刻字,还没法让别人修改的保险箱确实有点难度。那么怎么办呢?这其实困扰了人们很长的时间。直到有一天,人们发现:我们不一定非要在保险箱上刻规规矩矩的字,我们干脆在保险箱上刻手写名字好了。而且,刻字有点麻烦,干脆我们在上面弄张纸,让人直接在上面写,简单不费事。具体做法是,我们在保险箱上嵌进去一张纸,然后每个出产的保险箱都让权威机构的CEO签上自己的名字。然后,CEO把自己的签名公开在权威机构的“公告栏”上面。比如这个CEO就叫“学酥”,那么整个流程差不多是这个样子:
这个方法的本质原理是,每个人都能够通过笔迹看出保险箱上的字是不是学酥CEO签的。但是呢,这个字体是学酥CEO唯一的字体。别人很难模仿。如果模仿我们就能自己分辨出来了。要是实在分辨不出来呢,我们就请一个笔迹专家来分辨。这不是很好嘛。这个在密码学上就是数字签名。
上面这个签字的方法虽然好,但是还有一个比较蛋疼的问题。因为签字的样子是公开的,一个聪明人可以把公开的签字影印一份,自己造个保险箱,然后把这个影印的字也嵌进去。这样一来,这个聪明人也可以造一个相同签字的保险箱了。解决这个问题一个非常简单的方法就是在看保险箱上的签名时,不光看字体本身,还要看字体是不是和公开的字体完全一样。要是完全一样,就可以考虑这个签名可能是影印出来的。甚至,还要考察字体是不是和其他保险柜上的字体一模一样。因为聪明人为了欺骗大家,可能不影印公开的签名,而影印其他保险箱上的签名。这种解决方法虽然简单,但是验证签名的时候麻烦了一些。麻烦的地方在于我不仅需要对比保险箱上的签名是否与公开的笔迹一样,还需要对比得到的签名是否与公开的笔迹完全一样,乃至是否和所有发布的保险箱上的签名完全一样。有没有什么更好的方法呢?
当然有,人们想到了一个比较好的方法。那就是,学酥CEO签字的时候吧,不光把名字签上,还得带上签字得日期,或者带上这个保险箱的编号。这样一来,每一个保险箱上的签字就唯一了,这个签字是学酥CEO的签名+学酥CEO写上的时间或者编号。这样一来,就算有人伪造,也只能伪造用过的保险箱。这个问题就彻底解决了。这个过程大概是这么个样子:
3 造价问题(密钥封装机制,Key Encapsulation Mechanism)
解决了上面的各种问题,我们要考虑考虑成本了… 这种能“撞”门的保险箱虽然好,但是这种锁造价一般来说要比普通的锁要高,而且锁生产时间也会变长。在密码学中,对于同样“结实”的锁,能“撞”门的锁的造价一般来说是普通锁的上千倍。同时,能“撞”门的锁一般来说只能安装在小的保险柜里面。毕竟,这么复杂的锁,装起来很费事啊!而普通锁安装在多大的保险柜上面都可以呢。如果两个人想传输大量数据的话,用一个大的保险柜比用一堆小的保险柜慢慢传要好的多呀。怎么解决这个问题呢?人们又想出了一个非常棒的方法:我们把两种锁结合起来。能“撞”上的保险柜里面放一个普通锁的钥匙。然后造一个用普通的保险柜来锁大量的数据。这样一来,我们相当于用能“撞”上的保险柜发一个钥匙过去。对方收到两个保险柜后,先用自己的钥匙把小保险柜打开,取出钥匙。然后在用这个钥匙开大的保险柜。这样做更棒的一个地方在于,既然对方得到了一个钥匙,后续再通信的时候,我们就不再需要能“撞”上的保险柜了啊,在以后一定时间内就用普通保险柜就好了,方便快捷嘛。
以下参考 数字签名、数字证书、SSL、https是什么关系?
4.数字签名(Digital Signature)
数据在浏览器和服务器之间传输时,有可能在传输过程中被冒充的盗贼把内容替换了,那么如何保证数据是真实服务器发送的而不被调包呢,同时如何保证传输的数据没有被人篡改呢,要解决这两个问题就必须用到数字签名,数字签名就如同日常生活的中的签名一样,一旦在合同书上落下了你的大名,从法律意义上就确定是你本人签的字儿,这是任何人都没法仿造的,因为这是你专有的手迹,任何人是造不出来的。那么在计算机中的数字签名怎么回事呢?数字签名就是用于验证传输的内容是不是真实服务器发送的数据,发送的数据有没有被篡改过,它就干这两件事,是非对称加密的一种应用场景。不过他是反过来用私钥来加密,通过与之配对的公钥来解密。
第一步:服务端把报文经过Hash处理后生成摘要信息Digest,摘要信息使用私钥private-key加密之后就生成签名,服务器把签名连同报文一起发送给客户端。
第二步:客户端接收到数据后,把签名提取出来用public-key解密,如果能正常的解密出来Digest2,那么就能确认是对方发的。
第三步:客户端把报文Text提取出来做同样的Hash处理,得到的摘要信息Digest1,再与之前解密出来的Digist2对比,如果两者相等,就表示内容没有被篡改,否则内容就是被人改过了。因为只要文本内容哪怕有任何一点点改动都会Hash出一个完全不一样的摘要信息出来。
5.数字证书(Certificate Authority)
数字证书简称CA,它由权威机构给某网站颁发的一种认可凭证,这个凭证是被大家(浏览器)所认可的,为什么需要用数字证书呢,难道有了数字签名还不够安全吗?有这样一种情况,就是浏览器无法确定所有的真实服务器是不是真的是真实的,举一个简单的例子:A厂家给你们家安装锁,同时把钥匙也交给你,只要钥匙能打开锁,你就可以确定钥匙和锁是配对的,如果有人把钥匙换了或者把锁换了,你是打不开门的,你就知道肯定被窃取了,但是如果有人把锁和钥匙替换成另一套表面看起来差不多的,但质量差很多的,虽然钥匙和锁配套,但是你却不能确定这是否真的是A厂家给你的,那么这时候,你可以找质检部门来检验一下,这套锁是不是真的来自于A厂家,质检部门是权威机构,他说的话是可以被公众认可的(呵呵)。
同样的, 因为如果有人(张三)用自己的公钥把真实服务器发送给浏览器的公钥替换了,于是张三用自己的私钥执行相同的步骤对文本Hash、数字签名,最后得到的结果都没什么问题,但事实上浏览器看到的东西却不是真实服务器给的,而是被张三从里到外(公钥到私钥)换了一通。那么如何保证你现在使用的公钥就是真实服务器发给你的呢?我们就用数字证书来解决这个问题。数字证书一般由数字证书认证机构(Certificate Authority)颁发,证书里面包含了真实服务器的公钥和网站的一些其他信息,数字证书机构用自己的私钥加密后发给浏览器,浏览器使用数字证书机构的公钥解密后得到真实服务器的公钥。这个过程是建立在被大家所认可的证书机构之上得到的公钥,所以这是一种安全的方式。
常见的对称加密算法有DES、3DES、AES、RC5、RC6。非对称加密算法应用非常广泛,如SSH,
HTTPS, TLS,电子证书,电子签名,电子身份证等等。
参考 DES/3DES/AES区别