第四章 公钥
回顾第一章的场景:
小强与小红加密约会后,他需要订餐,但是他与餐厅客服没有见过面,不可能事先约定好密钥,虽然小强也可以使用与小红的密钥加密,但是餐厅客服也不清楚小强与小红之间的密钥“1999111120000520”,那通过邮件把密钥发送给餐厅客服?不行,这样也会被窃听者小白获取到。所以密钥怎么给别人?何时给别人?这个就是密钥配送问题了,由于加密与解密使用的是相同的密钥,所以称为对称密码。如果与其他人通信均使用相同的密钥,那么这个密钥其实也就没有那么的保密了,所以现代密码技术引入一个解决密钥配送的方法,叫做公钥密码。
4.1 什么是公钥密码
密钥分为加密密钥和解密密钥两种,发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。加密密钥是任意公开的,因此加密密钥称为公钥,解密密钥绝对不能够公开,因此解密密钥称为私钥。同时,公钥和私钥是一对,由公钥进行加密的密文,必须使用该公钥对应的私钥才能够解密,也就是说公钥和私钥之间具有数学上非常密切的关系。
4.2 公钥密码的流程
公钥密钥的流程如下图:
1、接收者餐厅客服生成一对密钥,一个公钥,一个私钥,私钥必须保密,自己保管好;
2、接收者餐厅客服公布公钥,可以全世界公布,也可以谁要给谁,注意这里是餐厅客服的公钥,不是小强的公钥,也不是私钥;
3、发送者小强使用餐厅客服的公钥和算法(算法一般都是公开了)对“晚上6点,情侣套餐”这个信息进行加密;
4、发送者小强发送密文,就是发邮件;
5、接收者餐厅客服收到密文后,使用自己的私钥,即餐厅客服私钥和算法对密文进行解密。
以上就是基本的流程,与正常理解有点不同,最需注意的一点:谁接收,用其钥。
那算法是什么?密钥对是什么?能够通过公钥破解出私钥吗?接下来我们以最常见的公钥密钥算法RSA为例进行讨论。
4.3 RSA
1977年的一天,阳光明媚,普照大地,三位麻省理工学院的数学家罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起设计了一种算法,可以实现非对称加密,这个算法用他们三个人的名字命名,叫做RSA算法。直至今日,在公开密钥加密和电子商业中RSA被广泛使用。从提出到现今的四十多年里,经历了各种攻击的考验,逐渐为人们接受。
1、密钥对的生成过程
第一步:随机选择两个不相等的质数p和q,注意p和q越大,就越难破解。我们这里为了演示方便,选择p=17,q=19。
第二步:计算p和q的乘积n。
第三步:计算n的欧拉函数φ(n)。
上一章我们介绍了:
所以:
由于p和q都是质数,根据欧拉定理:
代入则
第四步:随机选择一个整数e,e必须满足1< e < φ(n),且e与φ(n) 互质。
与288互质的数有很多,如:5,7,11,13,17,19,23……为了演示方便,也为了我能够偷懒一下,我这里选择了13,在实际应用中,是选择比较大的数。
那么公钥出来了,这里记录一下
第五步:计算私钥,就是e对于φ(n)的模反元素d。
定义:有一个整数d,使得e乘以d除以φ(n)的余数为1,即
那么d就是e对于φ(n)的模反元素。因为e与φ(n) 互质,所以d是一定存在,下面是推导过程证明d是存在的:
那么可以设一个整数k,上面的公式等价于:
将e=13,φ(n)=288代入得到:
设x=d,y=-k,得出下面的二元一次方程,此方程可以根据扩展欧几里得算法求解:
扩展欧几里得算法(辗转相除法)比较有难度不做解释,可以使用下面的python代码计算,该代码采用递归算法,比较烧脑:
#ax+by=q,输入a,b,可计算x,y,q的值
def ext_ojld(a, b):
if b == 0:
return 1, 0, a
else:
x, y, q = ext_ojld(b, a % b)
# q = gcd(a, b) = gcd(b, a%b)
x, y = y, (x - (a // b) * y)
return x, y, q
print(ext_ojld(13,288))
计算得出
(133, -6, 1)
所以,x=d=133
那么私钥出来了,这里记录一下
2、RSA加密解密算法
我们上面计算出e=13,n=323,d=133
RSA定义了密钥对如下:
公钥 =(e,n)
私钥 =(d,n)
RSA加解密公式如下:
我们来验证一下,假设明文为88,使用公钥e=13,n=323加密,计算得到密文为:
我们拿到密文为12,使用私钥d=133,n=323,计算明文为:
注:12的133次方很大,windows自带的计算器大数溢出,但是也不妨碍我们做mod运算。
是不是很神奇?在现实中,e,d这2个数都是比较大的,大到二进制的1024位,也就是2的1024次方,范围要这么大
(1-179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216)
所以,接下来我们讨论一下可靠性问题。
3、RSA可靠性
在整个密钥的生成过程中,我们使用到的数字:p,q,n,φ(n),e,d这6个数字,e,n是公开的,p,q,φ(n),d是不公开的,我们必须保护好d,那么怎么求得d?我们倒着推导尝试:
第一步:
如果e与φ(n)知道,就可以算出d,但是e已知,φ(n)不知,
第二步:
要知道φ(n),就必须知道p和q,但是p和q不知。
有人会说欧拉函数
n已知可求φ(n),但是此公式n必须为质数,显然n不是质数。
第三步:
我们知道n,但是p和q都是质数,将n进行质因数分解就可以破解,但是世界上还没有发现对大整数进行质因数分解的高效算法,这是一个数学难题,所以只要n最够大,分解得到p和q进而破译d是非常困难的。
下面是人类已经分解的最大整数(768)个二进制位,十进制是232位:
33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
×
36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917
比它更大的质因数分解,还没有被报道过,可以说目前能被破解的最长RSA密钥就是768位。
4、现实破译场景难题
还有另外一种破解思路,回顾小强订餐的场景,结合上述介绍的计算机结果,网络上的黑客小白,想要破解餐厅客服的私钥,他就使用餐厅客服的公钥e=13,n=323加密“88”的数据信息,得到的密文是“12”。
根据解密公式:
n已知,明文已知,密文已知,从而想计算d,那么小白就回到第三章讲的离散对数问题了。
上述例子中
此例子d=133如此简单,小白也要从1尝试到133才能找出解,当数字很大时,求离散对数非常困难。
虽然说世界最快的计算机“天河二号”超级计算机每秒3386千万亿次,但是现实中的d(私钥)为2的1024次方,多少个0,或者说多少个亿亿亿我就数不清了,就算是“天河二号”也要计算机好多好多年才能试出来。总之,世界上还没有任何可靠的攻击RSA算法的方式,只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。
4.4 RSA简单实现
本章最后,留下一段实现RSA算法的简单代码,模拟小强发给餐厅客服的信息“晚上6点,情侣套餐”。
# -*- coding:utf-8 -*-
import rsa
def rsa_encrypt(d_str):
#随机生成私钥和公钥,接受字符串进行加密,n取65537
#密钥取1024位
(pubkey, privkey) = rsa.newkeys(1024)
print('公钥:',pubkey)
print('私钥:',privkey)
# 将字符串进行编码
content = d_str.encode('utf-8')
print('编码结果:', content)
# 公钥加密
crypto = rsa.encrypt(content, pubkey)
print ('公钥加密结果:', crypto)
return crypto, privkey
def rsa_decrypt(crypto, privkey):
#解密
content = rsa.decrypt(crypto, privkey)
#解码
content = content.decode('utf-8')
print ('解密结果:', content)
if __name__ == '__main__':
text = rsa_encrypt('晚上6点,情侣套餐')
rsa_decrypt(*text)
公钥: PublicKey(105079188695616957771490959672726773255912338751210201441080996911380237326305229798263817427620244151408314608669473769086923837281673858555101749374949397069260318259038797336696696976391420476730356820672965255633222806806624337578979947200880419937813190042183191950773614012585879047976309664245061911451, 65537)
私钥: PrivateKey(105079188695616957771490959672726773255912338751210201441080996911380237326305229798263817427620244151408314608669473769086923837281673858555101749374949397069260318259038797336696696976391420476730356820672965255633222806806624337578979947200880419937813190042183191950773614012585879047976309664245061911451, 65537, 37228339446778615781867473223385918614355686001256537028860943700281333147374477481207372581166584508774427527941414947959614943891895956815095390196867359738402500762267126865118255027965136039390219929118742490020985853538544389322975147935090596832497723957646893904696904247800596707361403644190280665497, 40113909035504348618647542326483225754148397069818955172387388312727259744423634334737561447724477196285393842358669811957004560906513751626151745966420866238823207, 2619520042352706263911255855433453859505654047137594144652892967336222372126672087061258669856523656626830784781503394840931445283136169278147693)
编码结果: b'\xe6\x99\x9a\xe4\xb8\x8a6\xe7\x82\xb9\xef\xbc\x8c\xe6\x83\x85\xe4\xbe\xa3\xe5\xa5\x97\xe9\xa4\x90'
公钥加密结果: b'<\x8ff\xb0}\x80\xb3\xe5\xd2\xc3\xfa\x8a\x15$v\x15\xeeN\x1a\xebu&\xbc;D\x074xvM:R%\xcd,m\xef\x8b\xf4C\x1c\x9a[\x9f\x0bW"%e\x12\xcb:`\x86&k\xf68\xdb\xfe\x86\xb0I\x0e?E\xf4\xcc\xd6\x19HM\xa0?f\xcc\xf0\x9b\xb2\xef\xa6\x8a\xd0\xb8dh\xd2W \x9bmy\xd9\xc8\x98\xac\xbf\xacX\xdf\xc2\x1f\x00-\xbe9(.Z\x16\xba\x8a\xe0B;\xcb#\x9e"\xe6\x16x\xd54\x1d\xfel@'
解密结果: 晚上6点,情侣套餐