RSA算法原理詳解

一些相關的數學概念

在理解RSA算法以前,咱們必須理解一些相關的數學概念。html

質數及互質

質數(Prime number)又稱素數,指在大於1的天然數中,除了1和該數自身外,沒法被其餘天然數整除的數。大於1的天然數若不是素數,則稱之爲合數web

若是兩個或兩個以上的整數的最大公約數是 1,則稱它們爲互質(也叫互素)。兩個整數 a 與 b 互質,記爲 a ⊥ b。算法

互質的兩個數,有以下性質swift

整數a和b互質當且僅當存在整數x,y使得xa+yb=1。安全

也就是說,若是a、b互質,那麼xa+yb=1必然有解。若是xa+yb=1有解,則a、b必然互質;若是xa+yb=1無解,則a、b必然不是互質。bash

這個性質涉及擴展歐幾里德算法,咱們後面會提到。網絡

互質的判別方法主要有:函數

  • 兩個不一樣的質數必定互質。例如,2與七、13與19。
  • 較大的數是質數,則兩數互質。如33與51
  • 一個素數,另外一個不爲它的倍數,這兩個數互質。例如,3與十、5與 26。
  • 相鄰兩個天然數互質。即,若是p是大於1整數,則p與p-一、p+1互質。如15與1六、14互質
  • 相鄰兩個奇數互質。如19與2一、17互質
擴展歐幾里得算法

擴展歐幾里得算法,能夠用於咱們後面說的模反元素的計算,及一些性質的證實。性能

歐幾里得算法

歐幾里得算法,又叫展轉相除法,是求最大公約數的一種算法。ui

假設

a = q*b + r
複製代碼

其中a、b、q、r都是整數,gcd爲計算公約數函數,則

gcd(a,b) = gcd(b,r)
複製代碼

gcd(a,b) = gcd(b,a%b)
複製代碼

這樣,咱們就能夠以log(n)的時間複雜度求解a、b的最大公約數。用swift實現爲:

func gcd(a:Int,b:Int)->Int{
    return b == 0 ? a : gcd(a:b, b:a%b)
}
複製代碼
擴展歐幾里得算法

擴展歐幾里德算法基本過程以下:

對於不徹底爲 0 的非負整數 a,b,gcd(a,b)表示 a,b 的最大公約數,必然存在整數對 x,y ,使得

a*x + b*y = gcd(a,b)
複製代碼

若是x0、y0是上述二元一次不定方程的解,那麼該方程的通解爲:

x = x0 + (b/gcd)*t
y = y0 – (a/gcd)*t
複製代碼
擴展歐幾里得算法的計算過程

若是已知任意整數a、b,求未知數x、y,使得:

a*x + b*y = gcd(a,b)
複製代碼

用swift實現爲:

static func gcdEx(a:Int,b:Int,x:inout Int,y:inout Int)->Int{
    if b == 0 { // 遞歸直到 b == 0
        x = 1
        y = 0
        return a
    }else{
        let r = gcdEx(a:b, b:a % b, x: &x, y: &y)
        let t = x
        x = y
        y = t - a/b * y
        return r
    }
}
複製代碼

計算過程,大體以下:

  • 每次取a=bb=a%b,進行相同歐幾里得擴展算法,而後壓棧。此時不知道x、y,因此x、y仍是初始值
  • 當碰到一個特例b=0時,由於0與任何數的公約數是該數自己。即,當b=0時,a * 1 + 0 * 0 = a,也就是說x=1,y=0。
  • 獲得x=1y=0以後,依次出棧。若是棧頂的解爲x一、y1,棧下面的一個解(前一個解)爲x0、y0,那麼兩組解有以下關係:
x0  = y1
y0  = x1 - (a0/b0) * y1
複製代碼
  • 最後棧低出棧,獲得原始的a、b的解:x、y

對與兩組解的關係,簡單證實以下:

對整數a0、b0,存在x0、y0使得:

a0 * x0 + b0 * y0 = gcd(a0,b0) (等式一)
複製代碼

咱們令

a1 = b0   (等式二)
b1 = a0%b0  (等式三)
複製代碼

對整數a一、b1,存在x一、y1使得:

a1 * x1 + b1 * y1 = gcd(a1,b1) (等式四)
複製代碼

在計算機中,有

a0 % b0 = a0 - (a0/b0) * b0 (等式五)
複製代碼

由歐幾里得算法有:

gcd(a0,b0) = gcd(b0,a0%b0)   (等式六)
複製代碼

聯立上面六個等式得:

x0  = y1
y0  = x1 - (a0/b0) * y1
複製代碼
歐拉函數
歐拉函數的定義

歐拉函數(Euler' totient function ):用φ(n)表示,是小於或等於n的正整數中與n互質的數的數目。

例如小於8的數中,與8互質的有一、三、五、7,一共4個,那麼φ(8)=4。

歐拉函數的一些定理
  • 當n爲1時,φ(1)=1

  • 當n爲質數時,φ(n)=n-1。由於,兩個數中較大一個數是質數,則兩個數互質。那麼質數n與全部小於本身的數互質。而小於n的正整數有n-1個

  • 若是整數m、n互質,則 φ(m*n)=φ(m)*φ(n);也就是說歐拉函數是積性函數

  • 當n爲奇數時,φ(2n)=φ(n)

  • 若是n是質數p的k次冪,則φ(n)=φ(p^k)=p^k-p^(k-1)=(p-1)p^(k-1),由於除了p的倍數外,其餘數都跟n互質。

歐拉函數的計算

將n表示爲若干質數的乘積

n = p1^k1 * p2^k2 * p3^k3 ... * pn^kn
複製代碼

其中p一、p二、p3...pn是都是質數

則歐拉函數通式爲:

φ(n)=n * (1-1/p1) * (1-1/p2) * (1-1/p3) ... * (1-1/pn)
複製代碼

由上式,咱們能夠實現一個求歐拉函數的方法:

static func euler(n:Int)->Int{
    var n  = n
    var i  = 2
    var result  = Double(n)
    while i*i <=  n {
        if n % i == 0 { // 若是i是n的因子
            result = result * (1.0 - 1.0 / Double(i))
            while n % i == 0 { // 除去n全部i因子
                n /= i
            }
            print("i=\(i) n=\(n) reuslt=\(result)")
        }
        i += 1
    }
    // 處理最後一個質因子
    result = result * (1 - 1 / Double(n))
    return Int(result)
}
複製代碼

該函數的時間複雜度爲O(sqrt(n))

須要注意的是:

  • 計算歐拉函數的核心是找到n全部的質因子,也就是對n進行因數分解
  • 因爲任何一個合數,它大於sqrt(n)的質因素最多隻有一個。咱們在函數末尾已經處理了最後一個質因子。因此只需遍歷到sqrt(n)便可。
模反元素
模運算

模運算,又稱取模、模除,它用mod表示,也就是求餘數運算。在程序裏一般表示爲%運算符。譬如:

17 mod 5 = 17 % 5 = 2
複製代碼
單位元素

單位元素的定義是:任意一個元素和單位元素作了某種二元運算以後,獲得的結果等於本來那個元素。

譬如加法的單位元素是0,任何數n與0進行加法運算後,獲得的仍是n

依此,我麼知道乘法的單位元素是1,矩陣乘法的單位元素是單位方陣

反元素

若是一個元素x和另外一個元素y作某種運算f,獲得的結果是該運算f的單位元素,那麼y就是x在這種狀況下的反元素。

譬如,3的加分反元素是-3,由於 3+(-3)= 0

3的乘法反元素是1/3,由於 3 * 1/3 = 1

模反元素

模反元素也叫模逆元。

按照上面咱們說的反元素,它應該是在模運算下的反元素。而模運算的單位元素是1,即任意數n有n%1=n。那麼若是整數a的模反元素是b,則有:

a % b = 1  <=>  a ≡ 1 (mod b)
複製代碼

而實際上,模反元素除了模運算以後還有乘法運算,描述的是三個數以前的關係。

,或者能夠說是在模運算下的乘法反元素

若是一個整數a對模數(同餘數)n的模反元素是b,則有

a * b = 1 (mod n)
複製代碼

也就是說,若是a、b的乘積,對n取模的餘數是1,即

(a * b) % n = 1
複製代碼

則,b是整數a對同餘數n的模反元素。

譬如,在同餘數爲n=5時,整數a=3的模反元素是b=2,由於(3*2)%5=1

必然存在係數k,使得:

a * b - k*n + 1 
複製代碼

這至關於一個二元一次方程:

a * x +  n * y = 1
複製代碼
模反元素的性質

整數 a 對模數 n 之模逆元存在的充分必要條件是 a 和 n 互質

也就是說,若是a 和 n 互質,那麼整數 a 對模數 n 的模反元素必然存在。反之,若是a、n最大公約數不是1,那麼模反元素必然不存在。

簡單證實一下:

若是a對模數n的模反元素爲x,根據上一節咱們推出的二元一次方程,有

a * x + n * y = 1   (等式一)
複製代碼

根據歐幾里得擴展算法有:

a * x + n * y = gcd(a,n)  (等式二)
複製代碼

若是a、n互質,則gcd(a,n)=1,那麼咱們前面的到的二元一次方程必然是有解的,模反元素d存在。

若是gcd(a,n) != 1,等式一和二是矛盾的,等式一無解。

模反元素的計算

根據上面的推倒,咱們能夠獲得一個二元一次方程:

a * d + (-k) * n = 1
複製代碼

對於這個二元一次方程,咱們能夠用擴展歐幾里得算法求解。

func gcdEx(a:Int,b:Int,x:inout Int,y:inout Int)->Int{
    if b == 0 {
        x = 1
        y = 0
        return a
    }else{
        // r = GCD(a,b) = GCD(b,a%b)
        // 遞歸直到a % b == 0
        let r = gcdEx(a:b, b:a % b, x: &x, y: &y)
        let t = x
        x = y
        y = t - a/b * y
        return r
    }
}
複製代碼

則,求整數a對於模數n的模反元素d,即a*d + (-k)*n = 1的解,有

static func modularInverse(a:Int,n:Int,d:inout Int,k:inout Int){
    var x : Int = 0
    var y : Int = 0
    _  = gcdEx(a: Int(a), b:Int(n), x: &x, y: &y)
    d  = x
    k  = -y
}
複製代碼

須要注意的是,這樣計算的到的模反元素d有多是負數,而咱們指望的是在整數範圍取值。也就是說,咱們指望d和k都是大於0的。即x=d=>0y=-k<=0

而二元一次不定方程的解,不止一個,咱們能夠根據通解:

x = x0 + (b/gcd)*t
y = y0 – (a/gcd)*t
複製代碼

找到知足咱們預期的最小的一組解,則求a對於同餘數n的模反元素

static func modularInverse(a:UInt,n:UInt)->UInt{
    var x : Int = 0
    var y : Int = 0
    _ = gcdEx(a: Int(a), b:Int(n), x: &x, y: &y)
    if x < 0  || y > 0{
        let t0 = ceil((0 - Float(x)) / Float(n))
        let t1 = ceil((0 - Float(y)) / Float(a))
        let t  = Int(max(t0, t1))
        x = x + Int(n) * t
        y = y - Int(a) * t
    }
    let d  = UInt(x)
    let k  = UInt(-y)
    print("modular inverse a=\(a) n=\(n) d=\(d) k=\(k)")
    return d
}
複製代碼
歐拉定理

歐拉定理,也稱費馬-歐拉定理,是一個關於同餘的性質。

歐拉定理代表,若n、a爲正整數,且n與a互質,則:

a ^ φ(n) = 1 (mod n)
複製代碼

即:

a ^ φ(n) % n = 1
複製代碼

也就是說a的φ(n)次方,對n取模獲得餘數是1.

由歐拉定理有:

a * a ^ (φ(n) - 1) = 1
複製代碼

也就是說,若是整數a、n互質,那麼必然存在a對於模數n的模反元素d

d = a ^ (φ(n) - 1)
複製代碼
費馬小定理

費馬小定理是歐拉定理的一個特殊狀況。也就是當模數n爲質數的狀況,此時:

φ(n) = n - 1
複製代碼

a ^ (n-1) = 1 (mod n)
複製代碼

密鑰的生成

  • 一、隨機選擇兩個不相等的大質數pq。假設咱們取
p=61,q=53
複製代碼
  • 二、計算pq的乘積nn的二進制長度是密鑰長度,通常是1024位,重要場合位2048位
n = p * q = 61 * 53 = 3233
複製代碼
  • 三、計算n的歐拉函數φ(n)

歐拉函數φ(n) 是小於或等於n的正整數中與n互質的數的數目。

一個數的歐拉函數,等於其各個因子的歐拉函數之積,即

φ(n) = φ(p*q) = φ(p) * φ(q)
複製代碼

由於質數與小於它的每個數,都構成互質關係。因此

φ(p) = p - 1
φ(q) = q - 1
複製代碼

則:

φ(n) = φ(p*q) = φ(p) * φ(q) = (p-1)(q-1) = 60 * 52 = 3120
複製代碼
  • 四、隨機選擇一個整數e,條件是1< e < φ(n),且e與φ(n) 互質。 假設咱們選擇了17,即e=17。(實際應用中,經常選擇65537。)

  • 五、計算e對於φ(n)的模反元素d。

ed ≡ 1 (mod φ(n))
複製代碼

17 * d ≡ 1 (mod 3120)  <=> 17 * d - k * 3120 = 1
複製代碼

用"擴展歐幾里得算法"求解,用咱們上面的函數modularInverse(a:n:)計算,最後獲得d=2753、k=15

  • 六、將(e,n)封裝成公鑰,(d,n)封裝成私鑰,即公鑰(17, 3233),私鑰(2753,3233)

加密算法

用公鑰(e,n)對明文M進行加密,獲得密文C

C = M^e mod n
複製代碼

若是假設M=65,以前咱們獲得的公鑰是(17,3233),則:

C = 65^17 % 3233  = 2790
複製代碼

須要注意的是:

  • 一、明文M必須是整數。而使用中M常常是字符串,咱們須要轉化爲相應的ascii值或unicode值,即轉化爲Data類型進行運算。
  • 二、M必須小於n。若是M大於n,咱們須要分塊進行運算。即若是n是1024位,若是M大於1024位,須要切分紅若干小於1024位的塊。然後面咱們會說到Padding,每塊還須要減去Padding的大小。

解密算法

用私鑰解密密文C,獲得明文M

M = C^d mod n
複製代碼

即:

M = 2790 ^ 2753 % 3233 = 65
複製代碼

簽名算法

用私鑰(d,n)對信息M進行簽名,獲得簽名S

S = M^d mod n
複製代碼

至關於用私鑰對信息進行加密

驗證簽名算法

驗證算法,先用公鑰(e,n)對簽名S進行下列運算獲得M'

M' = S^d mod n 複製代碼

至關於用公鑰對簽名進行解密

而後對比M和M',若是相等則驗證成功

算法的簡單證實

那麼,如何保證用用私鑰解密出來的數就是原來的明文呢。

根據加密規則

C ≡ M^e mod n (等式一)
複製代碼

即:

M^e % n = C
複製代碼

能夠寫成:

M^e - kn = C  (等式二)
複製代碼

將上式帶入解密算法:

M ≡ C^d mod n  (等式三)
複製代碼

M ≡ (M1^e - kn)^d mod n
複製代碼

則:

M^(ed) -k^dn^d - k2*n = M
複製代碼

化簡得

M^(ed) - (k^dn^(d-1) - k2)*n = M
複製代碼

至關於

M^(ed) - k3*n = M   
複製代碼

也就是求:

M^(ed) ≡ M (mod n)  (等式四)
複製代碼

密鑰生成時有:

ed ≡ 1 (mod φ(n))  (等式五)
複製代碼

ed = hφ(n) + 1   (等式六)
複製代碼

將上式帶入等式四,有

M^(hφ(n) + 1) =  M (mod n) (等式七)

複製代碼

化簡得

M^hφ(n)  = 1 (mod n) (等式八)
複製代碼

接下來,分紅兩種狀況證實上面這個式子。

######(1)m與n互質。

由於m與n互質,根據歐拉定理有:

M^φ(n) ≡ 1 (mod n) (等式九)
複製代碼

聯立等式8、等式九,有

hφ(n) = φ(n)
複製代碼

h=1時,等式成立,原式獲得證實。

######(2)m與n不是互質關係。

由於M與n不是互質關係,因此M與n必然有大於一得公約數。而n的是質數p與q的乘積,因此n因數只有p、q。那麼M與n的p、q之一必然是M與n的公約數。則 M=kpM=kq,必然有一個成立。

若是M同時是p、q的倍數,那麼M=ln>=n,與RSA加密對明文的要求M<n互斥。因此M對於p、q,確定是一個的倍數,與另外一個互質。

假設M是p的倍數,與q互質,則有

M = kp  (等式十)
複製代碼

由於M、q互質,由歐拉定理有

M^(φ(q) = 1 (mod q) (等式十一)
複製代碼

則:

M^(φ(q) = 1 + l*q 
複製代碼

兩邊同時取h次冪,有

(M^(φ(q))^h = (1 + l*q)^h (等式十二)
複製代碼

由於對(1 + l*q)^h拆分以後,只有第一項1不含n,因此

(1 + l*q)^h % q = 1
複製代碼

(1 + l*q)^h = 1 (mod q)  (等式十三)
複製代碼

聯立等式12、十三,有

(M^(φ(q))^h = 1 (mod q)  (等式十四)
複製代碼

h=j(p-1),並將M=kp(φ(q)=q-1帶入(等式十四),有

((kp)^(q-1))^(j(p-1)) ≡ 1 mod q (等式十五)
複製代碼

φ(n)=(p-1)(q-1)帶入上式,有

kp^jφ(n) ≡ 1 mod q => kp^(jφ(n) + 1)  ≡ kp (mod q)
複製代碼

有上面的等式六,有jφ(n) + 1 = ed,帶入上式,得

kp^ed = kp (mod q)
複製代碼

kp^ed   = tq + kp  (等式十六)
複製代碼

兩邊同時對p取模,

kp^ed % p  = tq % p + kp % p
複製代碼

tq % p = 0
複製代碼

由於p、q互質,因此t必然是p的倍數,即

t = rq
複製代碼

帶入等式十六

kp^ed   = rpq + kp
複製代碼

由於M=kpn=pq,因此:

M^ed = rn + M => M^ed ≡ M (mod n)
複製代碼

在生成密鑰時

ed ≡ 1(modφ(n)) => ed=hφ(n)+1
複製代碼

M^(hφ(n)+1) ≡ M (mod n) 
複製代碼

M^hφ(n)  ≡  1 (mod n)
複製代碼

最後證實等式八成立,原式得以證實。

算法的安全性

(n,e)做爲公鑰,是能夠經過網絡公開傳播的。最主要是保證私鑰(n,d)的安全性。而n是在公鑰和私鑰中都存在的,那麼d就最爲關鍵了。

算法的安全性取決於由(n,e)計算出d的難度。而e咱們已經知道,只要求出(n),就能獲得d。

前面咱們給出了求φ(n)的一種算法,屬於暴力破解,它的時間複雜度爲O(sqr(n))。看似很簡單,也很快,可是n每增長一位時間複雜度會指數增加,想象一下當n達到1024位時,須要的算力是多麼恐怖。

而對極大數進行質因數分解,至今仍是世界級難題。

即使是超級計算機,也很難有效對兩個質數相乘獲得的合數進行質因數分解,因此這樣的原理能夠用於加密算法。

當合數全部的因子都很大時,採用強力方式獲得具體的因子是很困難的,而這也正是 RSA體制理論的核心。

截止2000年,RSA模數分解的最大位數是768位。

截至目前,分解 1024 bit 以上的 RSA number 仍然是一項耗資巨大的工程難題,大整數分解問題仍然被認爲是困難的。

因此,目前來講普通應該場景採用1024位的是相對安全的,一些比較機密的場景須要採用2048位。

咱們能夠在蘋果的鑰匙串中看到,大部分RSA整數基本都已是2018。而一般咱們應用時大多采用1024位就夠了。位數越多加解密消耗的性能越高。

參考資料

相關文章
相關標籤/搜索