某些字符串的RSA算法失败

问题描述

艾伦·B·唐尼(Allen B. Downey)在Chapter 11 of Think Python进行的练习11.7提示:

大整数的求幂是公用密钥加密的通用算法的基础。阅读RSA算法3的Wikipedia页面,并编写函数以对消息进行编码和解码。

我翻译了Key Generation section of the Wikipedia article中的简单英语算法。函数RSA接受一个字符串并将其编码为ASCII整数,创建用于解密的公共密钥和私有密钥,使用公共密钥加密编码的整数,使用私有密钥解密,然后将整数解码回字符串:

import math

def encode(s):

    m = ''
    for c in s:

        # each character in the message is converted to a ascii decimal value
        m += str(ord(c))

    return int(m)

def decode(i):
    # convert decrypted int back to string
    temp = str(i)
    decoded = ''
    while len(temp):
        if temp[:1] == '1':
            letter = chr(int(temp[:3]))
            temp = temp[3:]
        else:
            letter = chr(int(temp[0:2]))
            temp = temp[2:]
        decoded += letter
    
    return decoded

def generate_keys(m):

    # requires an int m that is an int representation (in ascii) of our message

    # 1. Choose two distinct prime numbers p and q.

    ##### TEST WIKIPEDIA EXAMPLE #####
    # p,q = 61,53

    # find two prime numbers whose product n will be greater than m
    i = math.ceil(math.sqrt(m))
    primes = []
    while len(primes) < 2:
        if isPrime(i):
            primes += [i]
        i += 1
    p,q = primes[0],primes[1]

    # 2. Compute n = pq.
    n = p * q
    in_range = int(m) < n
    if not in_range:
        print('m must be less than n')
        exit()

    # 3. Compute λ(n),where λ is Carmichael's totient function. Since n = pq,λ(n) = lcm(λ(p),λ(q)),and since p and q are prime,λ(p) = φ(p) = p − 1 and likewise λ(q) = q − 1. Hence λ(n) = lcm(p − 1,q − 1).
    # The lcm may be calculated through the Euclidean algorithm,since lcm(a,b) = |ab|/gcd(a,b).
    a,b = (p - 1),(q - 1)
    lam = int(abs(a * b) / math.gcd(a,b))
    # print('lam:',lam)

    # 4. Choose an integer e such that 1 < e < λ(n) and gcd(e,λ(n)) = 1; that is,e and λ(n) are coprime.

    ##### TEST WIKIPEDIA EXAMPLE #####
    # e = 17

    # accordng to wikipedia,the smallest (and fastest) possible value for e is 3: https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation
    i = 3

    while i < lam:
        if math.gcd(i,int(lam)) == 1:
            e = i
            break
        i += 1

    # 5. compute d where d * e ≡ 1 (mod lam)
    d = int(modInverse(e,lam))

    return {'public': [n,e],'private': d}

def encrypt_message(m,public):

    n,e = public[0],public[1]

    # generate ciphertext
    return pow(m,e,n)

def decrypt_message(cipher,n,d):

    # decrypt ciphertext
    return pow(cipher,d,n)

def RSA(s):
    m = encode(s)
    keys = generate_keys(m)
    cipher = encrypt_message(m,keys['public'])
    message = decrypt_message(cipher,keys['public'][0],keys['private'])

    messages_match = message == m
    if not messages_match:
        print('the decoded integer does not equal the encoded integer')
        exit()

    return decode(message)

# taken from https://www.geeksforgeeks.org/python-program-to-check-whether-a-number-is-prime-or-not/
def isPrime(n) : 
  
    # Corner cases 
    if (n <= 1) : 
        return False
    if (n <= 3) : 
        return True
  
    # This is checked so that we can skip  
    # middle five numbers in below loop 
    if (n % 2 == 0 or n % 3 == 0) : 
        return False
  
    i = 5
    while(i * i <= n) : 
        if (n % i == 0 or n % (i + 2) == 0) : 
            return False
        i = i + 6
  
    return True

# modInverse(a,m) taken from https://www.geeksforgeeks.org/multiplicative-inverse-under-modulo-m/
def modInverse(a,m) : 
    m0 = m 
    y = 0
    x = 1
  
    if (m == 1) : 
        return 0
  
    while (a > 1) : 
  
        # q is quotient 
        q = a // m 
  
        t = m 
  
        # m is remainder now,process 
        # same as Euclid's algo 
        m = a % m 
        a = t 
        t = y 
  
        # Update x and y 
        y = x - q * y 
        x = t 
  
  
    # Make x positive 
    if (x < 0) : 
        x = x + m0 
  
    return x 

if __name__ == "__main__":
    print(RSA('python')) # encrypts and decrypts message 'python'
    print(RSA('cat')) # encrypts and decrypts message 'cat'
    print(RSA('dog')) # encrypts and decrypts message 'dog'
    print(RSA('a 1')) # (to rule out spaces as the culprit) encrypts and decrypts message 'a 1'
    print(RSA('pythons')) # FAILS - 7 characters in string seems to be the limit
    print(RSA('hello world')) # FAILS - encoded string does not equal decoded string

在脚本结尾,我用几个字符串测试RSA。输出等于“ python”,“ cat”,“ dog”和“ a 1”(预期行为)的输入。但是'pythons'和'hello world'的编码和解码整数是不同的。

我怀疑是导致问题的原因是输入字符串的长度,但是我不确定在哪里导致故障。我的猜测是,如果pow(cipher,n)的长度太大,函数decrypt_message中的cipher将返回意外结果。

RSA为什么对某些字符串有效而对其他字符串无效?是输入字符串的长度还是其他?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...