Python中的RSA加密
我决定用Python写一个简单的RSA加密程序,但每次运行时在解密的时候,都会出现错误 IndexError: list out of range
,而且是在 find_key
这个地方。
这是错误信息:
p 937 q 353 n 330761 phi 329472 e 5 d 264609 Traceback (most recent call last): File "rsa.py", line 94, in print dec_rsa(b, d, n) File "rsa.py", line 88, in dec_rsa char_array.append(decrypt_byte(i, d, n)) File "rsa.py", line 77, in decrypt_byte return find_key(alpha, (c**d)%n) File "rsa.py", line 67, in find_key return [k for k, v in dic.iteritems() if v == val][0] IndexError: list index out of range
这是代码:
import fractions, sys, random, math
def isPrime( no ):
if no < 2: return False
if no == 2: return True
if not no&1: return False
for x in range(3, int(no**0.5)+1, 2):
if no%x == 0:
return False
return True
def primes_range(low, high):
primes = []
for i in range(high-low):
if isPrime(i+low):
primes.append(i+low)
return primes
let = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789~!@#$%^&*()_+'";:[]/<>,."
a, alpha = 2, {}
for i in let:
alpha[i] = a
a+=1
Low = 29
High = 1000
p = random.choice(primes_range(Low, High))
q = random.choice(primes_range(Low, High))
while p == q:
q = random.choice(primes_range(Low, High))
print "p ",p
print "q ",q
#p = 104729
#q = 3
p, q = int(p), int(q)
n = p*q
phi = (p-1)*(q-1)
print "n ",n
print "phi ",phi
for i in range(2, q if q>p else p):
if fractions.gcd(i, phi) == 1:
e = i
break
print "e ",e
def egcd(a,b):
u, u1 = 1, 0
v, v1 = 0, 1
while b:
q = a // b
u, u1 = u1, u - q * u1
v, v1 = v1, v - q * v1
a, b = b, a - q * b
return u, v, a
def modInverse(e, phi):
return egcd(e, phi)[0]%n
d = modInverse(e, n)
print "d ",d
def find_key(dic, val):
#print "val ",val
#print "dic ",list(dic.iteritems())
return [k for k, v in dic.iteritems() if v == val][0]
def encrypt_byte(byte, e, n):
try:
m = alpha[byte]
except:
m = int(byte)
return (m**e)%n
def decrypt_byte(c, d, n):
return find_key(alpha, (c**d)%n)
def enc_rsa(string, e, n):
char_array = []
for i in range(len(string)):
char_array.append(encrypt_byte(alpha[string[i]], e, n))
return char_array
def dec_rsa(enc_arr, d, n):
char_array = []
for i in enc_arr:
char_array.append(decrypt_byte(i, d, n))
return ''.join(char_array)
a = "hello, world"
b = enc_rsa(a, e, n)
#print b
print dec_rsa(b, d, n)
2 个回答
RSA的实现可以进一步简化,步骤如下:
1. 选择两个不同的大质数。为了简单起见,我们这里选择 p=937
和 q=353
,就像例子中那样。
2. 计算 n = p*q
。
3. 计算欧拉函数 φ(n) ≡ (p-1)*(q-1)
。
4. 选择一个与 φ(n)
互质的公钥 e
,为了简单,我们选择 e=5
,这是一个质数。
5. 计算私钥 d
,使得 d*e ≡ 1 (mod φ(n))
,可以使用乘法逆元算法(扩展欧几里得算法),具体可以参考 这里:
计算 a 模 n 的乘法逆元
# solution t to a*t ≡ 1 (mod n)
def multiplicative_inverse(a, n):
t, newt = 0, 1
r, newr = n, a
while newr != 0:
q = r // newr
t, newt = newt, t - q * newt
r, newr = newr, r - q * newr
if t < 0:
t = t + n
return t
步骤 1-5 的 Python 代码:
p, q = 937, 353 # use large primes here
n = p*q
φ = (p-1)*(q-1)
e = 5 # choose public key e as a prime, s.t., gcd(φ, e) = 1
d = multiplicative_inverse(e, φ) # private key d
print(d)
# 131789
6. 在发送方使用接收方的公钥(e
)加密消息(明文)。
7. 接收方用他的私钥(d
)解密收到的密文。
下面的代码展示了如何进行加密和解密:
def rsa_encrypt(plain_text, e, n):
# ideally we should convert the plain text to byte array and
# then to a big integer which should be encrypted, but here for the sake of
# simplicity character-by-character encryption is done, which will be slow in practice
cipher_text = [ord(x)**e % n for x in plain_text]
return cipher_text
def rsa_decrypt(cipher_text, d, n):
decoded_text = ''.join([chr(x**d % n) for x in cipher_text])
return decoded_text
现在,让我们使用上述函数进行加密和解密:
plain_text = 'Hello world'
cipher_text = rsa_encrypt(plain_text, e, n)
print(cipher_text)
# [296543, 169726, 215626, 215626, 293167, 147571, 122732, 293167, 217253, 215626, 102687]
decoded_text = rsa_decrypt(cipher_text, d, n)
decoded_text
# Hello world
希望你在学习Python的过程中玩得开心!
有几点需要注意:
(1) 你的isPrime函数有问题:它认为1是质数,2和3不是,但实际上25、35、121、143、289、323、529、841、899都是质数。这样会导致后面出现问题。
(2) 你没有检查p和q是否相等。
(3) 你的alpha[str(byte)]应该改成alpha[byte](否则你会得到“96llo, worl5”这样的结果)。
(4) 你计算的乘法模逆是错的。你应该用modInverse(e, phi(n)),而不是modInverse(e, n);可以参考这个示例。
修正这些问题后,似乎就能正常工作了。
接下来这些不是错误,而是建议:你应该使用pow(c,d,n)而不是(c**d)%n;对于大数字,前者会快很多。此外,如果你想把字母转换成数字,而不在乎具体是什么数字,可以使用“ord”和“chr”函数,甚至不需要字典。总之,你可能想要交换字典中的键和值:现在你的find_key函数其实可以用列表来实现,因为你只是遍历所有的k,v对,直到找到匹配的。
希望这些对你有帮助!