你知道什么叫非对称吗?
RSA 加密算法是一种非对称加密算法。在公开密钥加密和电子商业中 RSA 被广泛使用;
1.随机选择两个不同大质数 p和 q,计算 N=p×q
2.根据欧拉函数,求得r=φ(N)=φ(p)φ(q)=(p−1)(q−1)
3.选择一个小于 r 的整数 e,使 e 和 r互质。并求得 e 关于 r 的模反元素,为 d,有 ed≡1 mod red≡1 mod r
4.将 p 和 q 的记录销毁;
此时,(N,e)
是公钥,(N,d)
是私钥;
首先需要将消息 m 以一个双方约定好的格式转化为一个小于 N,且与 N 互质的整数 n。如果消息太长,可以将消息分为几段,这也就是我们所说的分组加密,后对于每一部分利用如下公式加密:n^e≡c(modN)
;
利用密钥 d 进行解密:c^d≡n(modN)
;
调用os.urandom()
随机生成n字节的随机数
def getrandom(n):
data=urandom(n)
a=int(binascii.hexlify(data),16)
return a
首先进行第一次素性检验,检验100以内的素数是否为n的因数
def test1(n):
dic_small=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
# print(len(dic_small))
for x in dic_small:
if n%x==0:
return False
return True
用Miller-rabin素性检验算法检验n,检验k次
def test_Miller(n,k):
if n<2:
return False
d=n-1
r=0
while not(d&1):
r+=1
d>>=1
for _ in range(k):
a=getrandom(120)
x=pow(a,d,n)
if x==1 or x==n-1:
continue
for _ in range(r-1):
x=pow(x,2,n)
if x==1:
return False
if x==n-1:
break
else:
return False
return True
产生byte字节的数,并且通过两次素性检测
def getprime(byte):
while True:
n=getrandom(byte)
if test1(n):
if test_Miller(n,10):
pass
else:
continue
else:
continue
return n
产生一个和φ(n)互素的公钥e
def gete(ol):
e=getprime(1)
if ol%e!=0:
return e
加密时,先将明文进行16进制编码,然后转化为10进制计算
def hexencode(str0):
hexcode=base64.b16encode(str0)
res=int(hexcode,16)
return res
解密时需要对计算出来的明文编码进行16进制解码
def hexdecode(str0):
temp=""
for i in range(len(str0)):
if ord(str0[i])>96 and ord(str0[i])<123:
temp+=chr(ord(str0[i])-32)
else:
temp+=str0[i]
res=base64.b16decode(temp)
return res
def RSAencrypt_group(m,e,n):
num1=hexencode(m)
c=pow(num1,e,n) #cipher
return c
5个字符一组进行加密,将所有的密文连接在一起输出,将每个分组的密文存储在cip列表中用于以后的解密
def RSAencrypt_all(m,e,n):
res=""
for i in range(0,len(m),5):
temp=m[i:i+5]
group_cipher=RSAencrypt_group(temp,e,n)
cip.append(group_cipher)
res+=str(group_cipher)
return res
输入数字c进行RSA解密得到num1,然后再转化为16进制,再对16进制数进行解码变成明文
def RSAdecrypt_group(c,e,n,ol):
d=gmpy2.invert(e,ol)
num1=pow(c,d,n)
str1='{:0x}'.format(num1)
plain=hexdecode(str1)
return plain
def RSAdecrypt_all(e,n,ol):
res=""
for i in range(len(cip)):
temp=RSAdecrypt_group(cip[i],e,n,ol)
res+=temp
return res
理论证明:
即我们要证n^ed≡n(modN)
,已知ed≡1modϕ(N)
,那么 ed=kϕ(N)+1
,即需要证明
n^(kϕ(N)+1)≡n(modN)
这里我们分两种情况证明
第一种情况 gcd(n,N)=1,那么 n^ϕ(N)≡1modN
,因此原式成立。
第二种情况 gcd(n,N)!=1,那么 n 必然是 p 或者 q 的倍数,并且 n 小于 N。我们假设n=xp
那么 x 必然小于 q,又由于 q 是素数。那么n^ϕ(q)≡1modq
进而n^kϕ(N)=n^(k(p−1)(q−1))=(n^ϕ(q))^k(p−1)≡1modq
那么n^(kϕ(N)+1)=n+uqn
进而nkϕ(N)+1=n+uqxp=n+uxN
;所以原式成立。
程序正确性:
程序输入明文hello,加载出1024bit的大素数p和q,继续加载出公钥e,经过加密得到如下密文;就用上面得到的p,q,e,cipher,先计算出私钥d,再解密密文,得到如下明文hello,解密成功,算法正确性得到验证;
RSA 算法的可靠性由极大整数因数分解的难度决定。换言之,对一极大整数做因数分解愈困难,RSA 算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。如今,只有短的 RSA 密钥才可能被强力方式解破,RSA算法选取的两个安全的大素数目前推荐长度为至少为1024比特;到现在为止,还没有任何可靠的攻击 RSA 算法的方式。
当两个用户使用相同的模数 N、不同的私钥时,加密同一明文消息时即存在共模攻击;
设两个用户的公钥分别为 e1和 e2,且两者互质。明文消息为 mm,密文分别为:c1=m^e1(modN)
c2=m^e2(modN)
当攻击者截获 c1和 c2后,就可以恢复出明文。用扩展欧几里得算法求出 re1+se2=1modn
的两个整数 r 和 s,由此可得:
(c^r1)*(c^s2)≡(m^re1)*(m^se2)modn≡m^(re1+se2)modn≡m(modn)
就拿CTF比赛Crypto中的一道题目来说:给出两组密文 message1,message2;给出两组分别的公钥是e1和e2;给出两组加密用的是同一个n;如何去计算出明文呢?
{6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249,773}
{6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249,839}
message1=3453520592723443935451151545245025864232388871721682326408915024349804062041976702364728660682912396903968193981131553111537349
message2=5672818026816293344070119332536629619457163570036305296869053532293105379690793386019065754465292867769521736414170803238309535
可以看出两个公钥的 N 是一样的,并且两者的 e 互素。用上述原理写一个python脚本跑一下:
import gmpy2
n=6266565720726907265997241358331585417095726146341989755538017122981360742813498401533594757088796536341941659691259323065631249
e1 = 773
e2 = 839
message1=3453520592723443935451151545245025864232388871721682326408915024349804062041976702364728660682912396903968193981131553111537349
message2=5672818026816293344070119332536629619457163570036305296869053532293105379690793386019065754465292867769521736414170803238309535
# s & t
gcd, s, t = gmpy2.gcdext(e1, e2)
if s < 0:
s = -s
message1 = gmpy2.invert(message1, n)
if t < 0:
t = -t
message2 = gmpy2.invert(message2, n)
plain = gmpy2.powmod(message1, s, n) * gmpy2.powmod(message2, t, n) % n
print plain
得出结果明文:
1021089710312311910410111011910111610410511010710511610511511211111511510598108101125
这时候需要考虑当时明文是如何转化为这个数字了,这个应该是 ASCII 字符转换,进而我们使用如下代码得到 flag
i = 0
flag = ""
plain = str(plain)
while i < len(plain):
if plain[i] == '1':
flag += chr(int(plain[i:i + 3]))
i += 3
else:
flag += chr(int(plain[i:i + 2]))
i += 2
print flag
###flag{whenwethinkitispossible}###
公钥加密中密钥1024bit的密钥强度只能相当于对称加密中几十bit的强度,而且密钥的生成需要时间,所以还是对称加密的效率较高,而且耗时较短,速度比较快;一般通信中采用的就是明文采用对称加密的方法发出,而加解密密钥用公钥加密体制加密后发给接收方,这样效率会比较高;