RSA演算法原理

minjialong發表於2020-12-09

  1976年,兩位美國計算機學家 W h i t f i e l d D i f f i e Whitfield Diffie WhitfieldDiffie M a r t i n H e l l m a n Martin Hellman MartinHellman,提出了一種嶄新構思,可以在不直接傳遞金鑰的情況下,完成解密。這被稱為"Diffie-Hellman金鑰交換演算法"。這個演算法的產生,預示著非對稱加密誕生了。

  非對稱加密的場景是:A給B傳送資訊。

  1. B要先生成兩把金鑰(公鑰和私鑰)。公鑰是公開的,任何人都可以獲得,私鑰則是保密的。
  2. A獲取B的公鑰,然後用它對資訊加密。
  3. B得到加密後的資訊,用私鑰解密。

  1977年,三位數學家 R i v e s t Rivest Rivest S h a m i r Shamir Shamir A d l e m a n Adleman Adleman 設計了一種演算法,可以實現非對稱加密。這種演算法用他們三個人的名字命名,叫RSA演算法。從那時直到現在,RSA演算法一直是最廣為使用的"非對稱加密演算法"。毫不誇張地說,只要有計算機網路的地方,就有RSA演算法。這種演算法非常可靠,金鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA金鑰是232個十進位制位,也就是768個二進位制位,因此可以認為,1024位的RSA金鑰基本安全,2048位的金鑰極其安全,當然量子計算機除外。

RSA基本原理

  首先我們需要明白幾個基本概念

  1. 素數:又稱質數,指在一個大於1的自然數中,除了1和此整數本身外,不能被其他自然數整除的數。
  2. 互質,又稱互素。若N個整數的最大公因子是1,則稱這N個整數互質。
  3. 模運算即求餘運算。”模“是“ m o d mod mod”的音譯。和模運算緊密相關的一個概念是“同餘”。數學上,當兩個整數除以同一個正整數,若得相同餘數,則二整數同餘。
尤拉函式

  任意給定正整數n,請問在小於等於n的正整數之中,有多少個與n構成互質關係?(比如,在1到8之中,有多少個數與8構成互質關係?)計算這個值的方法就叫做尤拉函式,以 ϕ ( n ) \phi (n) ϕ(n)表示。

  比如,計算8的尤拉函式,和8互質的 1、2、3、4、5、6、7、8,所以 ϕ ( 8 ) = 4 \phi(8) = 4 ϕ(8)=4

  尤拉函式的幾個特性

  1. 如果n是質數的某一個次方,即 n = p k n = p^k n=pk (p為質數,k為大於等於1的整數),則 ϕ ( n ) = ϕ ( p k ) = p k − p ( k − 1 ) \phi(n)=\phi(p^k)=p^k-p^(k-1) ϕ(n)=ϕ(pk)=pkp(k1)

    比如: ϕ ( 8 ) = ϕ ( 2 3 ) = 2 3 − 2 2 = 8 − 4 = 4 \phi(8)=\phi(2^3)=2^3-2^2=8-4=4 ϕ(8)=ϕ(23)=2322=84=4

  2. 如果n是質數,則 ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n1 。因為質數與小於它的每一個數,都構成互質關係。

    比如:計算7的尤拉函式,7是質數, ϕ ( 7 ) = 6 \phi(7) = 6 ϕ(7)=6

  3. 如果n可以分解成兩個互質的整數之積,即$ n = p * k$ ,則 ϕ ( n ) = ϕ ( p ∗ k ) = ϕ ( p 1 ) ∗ ϕ ( p 2 ) \phi(n) = \phi(p * k) = \phi(p1)*\phi(p2) ϕ(n)=ϕ(pk)=ϕ(p1)ϕ(p2)

    比如: ϕ ( 56 ) = ϕ ( 8 ) ∗ ϕ ( 7 ) = 4 ∗ 6 = 24 \phi(56) = \phi(8) * \phi(7) = 4 * 6 = 24 ϕ(56)=ϕ(8)ϕ(7)=46=24

尤拉定理

  如果兩個正整數m和n互質,那麼m的 ϕ ( n ) \phi(n) ϕ(n)次方減去1,可以被n整除。
m Φ ( n ) m o d    n ≡ 1 m^{\Phi (n)} \mod n \equiv 1 mΦ(n)modn1

小費馬定理

  尤拉定理的特殊情況,如果兩個正整數m和n互質,而且n為質數!那麼φ(n)結果就是n-1
m ( n − 1 ) m o d    n ≡ 1 m^{(n-1)} \mod n \equiv 1 m(n1)modn1

模反元素

  如果兩個正整數e和x互質,那麼一定可以找到整數d,使得$ed-1 被 x 整 除 , 或 者 說 被x整除,或者說 xed$被x除的餘數是1。那麼d就是e相對於x的模反元素。
e ∗ d m o d    x ≡ 1 e*d \mod x \equiv 1 edmodx1

  以上基本原理都很淺顯,接下來稍微轉化一下
m Φ ( n ) m o d    n ≡ 1 (1) m^{\Phi (n)} \mod n \equiv 1 \tag{1} mΦ(n)modn1(1)

m k ∗ Φ ( n ) m o d    n ≡ 1 (2) m^{k*\Phi (n)}\mod n \equiv 1 \tag{2} mkΦ(n)modn1(2)

m k ∗ Φ ( n ) + 1 m o d    n ≡ m (3) m^{k*\Phi (n)+1}\mod n \equiv m \tag{3} mkΦ(n)+1modnm(3)

e ∗ d ≡ k ∗ x + 1 (4) e*d \equiv k*x+1 \tag{4} edkx+1(4)

m e ∗ d m o d    n ≡ m (5) m^{e*d} \mod n \equiv m \tag{5} medmodnm(5)

  公式3與公式4轉化為公式5,需要一個前提: e e e ϕ ( n ) \phi (n) ϕ(n)互質

  我們可以將公式5變換一下:
m e ∗ d m o d    n = = > ( m e ) d m o d    n (6) m^{e*d} \mod n ==> (m^e)^d \mod n \tag{6} medmodn==>(me)dmodn(6)

m e m o d    n ≡ c c d m o d    n ≡ m m^e \mod n \equiv c \\ c^d \mod n \equiv m memodnccdmodnm

  第一個用於加密,第二個用於解密。假設 m = 12 m=12 m=12(隨便取值,只要比n小就可以), n = 15 n=15 n=15(還是隨機取一個值,不一定非要互質), ϕ ( n ) = 8 \phi(n) = 8 ϕ(n)=8 e = 3 e=3 e=3( e e e必須和 ϕ ( n ) \phi(n) ϕ(n)互質),這裡取 d = 19 d=19 d=19 3 d − 1 = 8 3d-1=8 3d1=8,d也可以為3,11等等,也就是 d = ( 8 k + 1 ) / 3 d = (8k + 1) / 3 d=(8k+1)/3

演算法過程

  1. 隨意選擇兩個大的質數p和q,p不等於q,計算 N = p ∗ q N=p*q N=pq
  2. 根據尤拉函式,不大於N且與N互質的整數的個數為 ( p − 1 ) ( q − 1 ) (p-1)(q-1) (p1)(q1)
  3. 選擇一個整數e與 ( p − 1 ) ( q − 1 ) (p-1)(q-1) (p1)(q1)互質,並且e小於 ( p − 1 ) ( q − 1 ) (p-1)(q-1) (p1)(q1)
  4. 用以下這個公式計算d: d ∗ e m o d    ( p − 1 ) ( q − 1 ) ≡ 1 d*e \mod (p-1)(q-1) \equiv 1 demod(p1)(q1)1
  5. 將p和q的記錄銷燬。

  這裡得到的(N, e)是公鑰,(N, d)是私鑰。

  下面展示RSA的實現細節

import time


def range_prime(start, end):
    l = list()
    for i in range(start, end+1):
        flag = True
        for j in range(2, int(i**0.5)+1):
            if i % j == 0:
                flag = False
                break
        if flag:
            l.append(i)
    return l


def generate_keys(p, q):
    numbers = range_prime(10, 100)
    N = p * q
    C = (p-1) * (q-1)
    e = 0
    for n in numbers:
        if n < C and C % n > 0:
            e = n
            break
    if e == 0:
        raise BaseException("e not found")
    d = 0
    for n in range(2, C):
        if(e * n) % C == 1:
            d = n
            break
    if d == 0:
        raise BaseException("d not found")
    return (N, e), (N, d)


def quick_algorithm(a, b, c):
    a = a % c
    ans = 1
    while b != 0:
        if b & 1:
            ans = (ans * a) % c
        b >>= 1
        a = (a * a) % c
    return ans


def encrypt(m, key):
    C, x = key
    # return quick_algorithm(m, x, C)
    return (m ** x) % C


def decrypt(m, key):
    return encrypt(m, key)


def ord2hex(num):
    return hex(num)


if __name__ == '__main__':
    start = int(1e3)
    end = int(1e5)
    prime_list = []
    count = 0
    while len(prime_list) < 2:
        if count > 5:
            raise RecursionError("達到最大次數")
        prime_list = range_prime(start, end)
    a = prime_list[0]
    b = prime_list[-1]
    print(a, b)
    time1 = time.time()
    print("開始產生金鑰對")
    public_key, private_key = generate_keys(a, b)
    print("私鑰:", private_key)
    print("公鑰:", public_key)
    time2 = time.time()
    print("金鑰對生成結束,耗時:{}s".format(time2-time1))
    ord_list = []
    encode_list = []
    s_input = input("請輸入要加密的字串:")
    time3 = time.time()
    print("開始加密")
    for s in s_input:
        s_ord = ord(s)
        s_encode = encrypt(s_ord, public_key)
        ord_list.append(s_ord)
        encode_list.append(s_encode)
    time4 = time.time()
    print("加密完成,耗時:{}s".format(time4-time3))
    # print(ord_list)
    print(encode_list)
    print("原資料:", [ord2hex(num) for num in ord_list])
    print("加密後的資料:", ":".join([ord2hex(num) for num in encode_list]))
    print("開始解密")
    time5 = time.time()
    decode_list = []
    for num_encode in encode_list:
        ord_decode = decrypt(num_encode, private_key)
        decode_list.append(ord_decode)
    time6 = time.time()
    print("解密完成,耗時:{}s".format(time6-time5))
    print("解密後的資料:", [ord2hex(num) for num in decode_list])
    print("轉化為字串:", [chr(num) for num in decode_list])

相關文章