密碼學基礎1:RSA算法原理全面解析 1 數論基礎 2 RSA算法原理 3 RSA 密鑰格式實例分析 參考資料

網上寫 RSA 算法原理的文章不少,但是基本上要麼忽略了數學原理的說明,要麼缺少實際的可運行的例子,爲此特寫了此文,將 RSA 需要用到的數學概念和定理都總結了一番,並基於算法原理使用 python 實現了 RSA 密鑰生成和加解密的demo,希望對大家深入瞭解 RSA 算法有所幫助。本文第 1 節爲 RSA 相關的數論基礎,第 2 節是 RSA 算法原理描述和證明,第 3 節通過 openssl 生成 RSA 密鑰來分析實際應用中的密鑰格式。可以直接從第 2 節開始,遇到相關定理可以轉到第 1 節處查閱即可。本文所有代碼都在 shishujuan:rsa-algrithm,如有錯誤,也懇請指正。

1 數論基礎

本節內容中可能用到的符號說明如下:

  • \Bbb Z: 指代整數集合,包括正負整數和0。
  • \Bbb Z_n: 指代 0 到 n-1 之間的整數集合。
  • |a|:數 a 的絕對值。
  • ⌊a/n⌋: 指代小於等於 a/n 的最大整數,比如 ⌊5/3⌋ = 1,⌊8/3⌋ = 2 等。
  • 質數:質數在有些地方又翻譯爲素數,本文統一用質數表述。
  • p^{-1}_q:p 對模數 q 的模逆元素,即 \small p^{-1}_q p \equiv 1(mod ~q)
  • 乘法符號:文中可能在證明和示例中用了不同的乘法符號如 \times\cdot,意義一致。

1.1 質數和合數

質數和合數: 質數是指除了平凡約數1和自身之外,沒有其他約數的大於1的正整數。大於1的正整數中不是素數的則爲合數。如 7、11 是質數,而 4、9 是合數。在 RSA 算法中主要用到了質數相關性質,質數可能是上帝留給人類的一把鑰匙,許多數學定理和猜想都跟質數有關。

1.2 除法定理

[定理1] 除法定理: 對任意整數 a 和 任意正整數 n,存在唯一的整數 q 和 r,滿足 0<=r<n,a = qn + r。其中,q = ⌊a/n⌋ 稱爲除法的商,而 r = a ~mod ~ n 稱爲除法的餘數。

[證明] 除法定理

  • 存在性證明
    設集合 S=\{a - xn,x \in \Bbb Z\},則可以知道集合 S 中一定存在非負元素,設這個非負整數集合爲 T,根據自然數的良序法則,非空自然數集合中必然存在最小元素。設 r = a-qn 爲集合 T 中的最小元素,因爲集合 T 是非負整數集合,因此 r >= 0。而 r 必然滿足 r < n,因爲 r >= n \implies r-n>=0,則可以推出元素 r - n \in T。但是 r - n < r,這與我們定義的 r 是集合 T 中最小的元素矛盾,因此 r < n。這也就證明了 q 和 r 的存在性,即一定存在 q 和 r,且 0 <= r < n
  • 唯一性證明
    假設有兩組不同的整數q_1, r_1q_2, r_2滿足條件,則有 a = q_1n + r_1,a = q_2n + r_2( 0 <= r_1, r_2 < n)
    兩個式子相減可得
    r_2 - r_1 = n(q_1 - q_2)
    左邊絕對值 |r_2 - r_1| < n,但是右邊絕對值 |n(q_1-q_2)| 顯然是 n 的整數倍,因此只能 q_1 = q_2,繼而可得 r_1 = r_2,由此唯一性得證。

整除: 在除法定理中,當餘數 r = 0 時,表示 a 能被 n 整除,或者說 a 是 n 的倍數,用符號 n\ |\ a 表示。

1.3 約數、公約數和最大公約數

約數和倍數: 對於整數 d 和 a,如果 d\ |\ a,且 d >= 0,則我們說 d 是 a 的約數,a 是 d 的倍數。

公約數: 對於整數 d,a,b,如果 d 是 a 的約數且 d 也是 b 的約數,則 d 是 a 和 b 的公約數。如 30 的約數有 1,2,3,5,6,10,15,30,而 24 的約數有 1,2,3,4,6,8,12,24,則 30 和 24 的公約數有 1,2,3,6。其中 1 是任意兩個整數的公約數。

公約數的性質:

  • d | a,且 d | b => d | (a+b)d | (a-b)。更一般的,對任意整數 x 和 y,有:d | a,且 d | b => d | (ax + by)。 (1.1)
  • a | b,則要麼 b = 0,要麼 |a| <= |b|,即 a | bb | a 可以推出 |a| = |b|。 (1.2)

最大公約數: 兩個整數最大的公約數稱爲最大公約數,用 gcd(a,b) 來表示,如 30 和 24 的最大公約數是 6。gcd(a, b) 有一些顯而易見的性質:

gcd(a, b) = gcd(b, a) \quad(1.3)
gcd(a, 0) = |a| \quad(1.4)
gcd(ka, a) = |a| \quad(1.5)

[定理2] 最大公約數定理: 如果 a 和 b 是不爲0的整數,則 gcd(a, b) 是 a 和 b 的線性組合集合 {\lbrace ax + by: x, y \in \mathbb{Z} \rbrace} 中的最小正元素。

[證明] gcd(a, b) 是 a 和 b 線性組合集合最小正元素。

設 s 是 a 和 b 的線性組合集合中最小正元素,且對某個 x,y,有 s = ax + by,設 q = ⌊a/s⌋,則 a\ mod\ s = a - qs = a - q(ax + by) = a(1-qx) + b(-qy),因此 a\ mod\ s 也是 a 和 b 的一種線性組合。由於 0 <= a\ mod\ s < s,所以只能 a\ mod\ s = 0,不然就與我們假設矛盾了。由 a\ mod\ s = 0,於是 s | a,類似可以得到 s | b,於是 s 是 a 和 b 的公約數,於是根據定義,gcd(a, b) >= s

另一方面,因爲 gcd(a, b) 可以同時被 a 和 b 整除,而 s 是 a 和 b 的一個線性組合,由性質 1.1 可得 gcd(a, b) | s,另外 s > 0,由性質 1.2 可知 gcd(a, b) <= s。結合前面的 gcd(a, b) >= s,可證得 s = gcd(a,b)

由定理2可以得到一個推論:

[推論1] 對任意整數 a 和 b,如果 d\ |\ ad\ |\ b,則 d\ |\ gcd(a, b)

推論1的證明比較簡單,由定理2可知 gcd(a,b) 是 a 和 b 的一個線性組合,故由性質 1.1 可得證 d\ |\ gcd(a,b)

1.4 互質數

互質數: 如果兩個整數 a 和 b 只有公因數 1,即 gcd(a, b) = 1,則我們就稱這兩個數是互質數(coprime)。比如 4 和 9 是互質數,但是 15 和 25 不是互質數。

互質數的性質:

  • 1與任何正整數都是互質數。(1.6)
  • 任何兩個質數都是互質數。 (1.7)
  • 質數與小於它的每一個數都是互質數。(1.8)

1.5 歐幾里得算法

歐幾里得算法分爲樸素歐幾里得算法和擴展歐幾里得算法,樸素法用於求兩個數的最大公約數,而擴展的歐幾里得算法則有更多廣泛應用,如後面要提到的求一個數對特定模數的模逆元素等。

1.5.1 樸素歐幾里得算法

求兩個非負整數的最大公約數最有名的是 輾轉相除法,最早出現在偉大的數學家歐幾里得在他的經典鉅作《幾何原本》中。輾轉相除法算法求兩個非負整數的最大公約數描述如下:

gcd(a, 0) = a
gcd(a, b) = gcd(b, a\ mod\ b)(a, b \in \mathbb{Z},a,b >= 0)

例如,gcd(252, 105) = gcd(105, 42) = gcd(42, 21) = gcd(21, 0) = 21,在求解過程中,較大的數縮小,持續進行同樣的計算可以不斷縮小這兩個數直至其中一個變成零。

[證明] 歐幾里得輾轉相除法求GCD

由條件知 a >= b >= 0,令 r = a % b,a = bt + r,則需要證明 gcd(a, b) = gcd(b, r)

  • 首先,令 d = gcd(a, b),則根據 gcd 定義,d | ad | b。即 d | (bt + r)d | b,由此可得 d | r,則 d | gcd(b, r)。即 gcd(a, b) | gcd(b, r)

  • 其次,令 c = gcd(b, r),則根據 gcd 定義,c | b,c | r。因爲 a = bt + r,因此,c | a。於是 c | a,c | b,可得 c | gcd(a,b),即 gcd(b,r) | gcd(a,b)

  • 由此可得 gcd(a, b) | gcd(b, r)gcd(b, r) | gcd(a, b),由性質1.2 可得 gcd(a, b) = gcd(b, r)

歐幾里得算法的python實現如下:

gcd = lambda a, b: (gcd(b, a % b) if a % b else b)

1.5.2 擴展歐幾里得算法

擴展歐幾里得算法在 RSA 算法中求模反元素有很重要的應用,定義如下:

定義: 對於不全爲 0 的非負整數 a,b,則必然存在整數對 x,y,使得

ax + by = gcd(a, b)

例如,a 爲 3,b 爲 8,則 gcd(a, b) = 1。那麼,必然存在整數對 x, y,滿足 3x + 8y = 1。簡單計算可以得到 x = 3, y = -1 滿足要求。

[證明] 擴展歐幾里得算法

  • a > b,則當 b = 0 時,gcd(a, 0) = a,則 x = 1,y = 0 就是一組解。

  • a,b 都不爲 0 時,令 r = a\ mod\ b = a - ⌊a/b⌋b,由定理2,設 ax_1 + by_1 = gcd(a, b) 。此外,由定理2,設 bx_2 + ry_2 = gcd(b, r),根據 r 的定義改寫此等式,可以得到 bx_2 + (a - ⌊a/b⌋b)y_2 = gcd(b, r),即 gcd(b, r) = ay_2 + b(x_2-⌊a/b⌋y_2), 根據歐幾里得算法,gcd(a, b) = gcd(b, r),則令 x_1 = y_2,y_1 = x_2-⌊a/b⌋y_2 即可。由此可以得到求解 x,y 的遞歸解法。

擴展歐幾里得算法的python實現如下:

# 擴展歐幾里得算法實現,參數爲 a 和 b,返回最大公約數 d 和 線性組合中的參數 x,y。
def ext_gcd(a, b):
    if b == 0:
        return a, 1, 0

    d2, x2, y2 = ext_gcd(b, a % b)
    d, x, y = d2, y2, x2-a/b*y2
    return d, x, y

1.6 同餘和模運算

同餘: 對於正整數 n 和 整數 a,b,如果滿足 \small n | (a-b) ,即 a-b 是 n 的倍數,則我們稱 a 和 b 對模 n 同餘,記號如下:a \equiv b (mod ~ n) 例如,因爲 \small 12 | (13-1),於是有 \small 13 \equiv 1 (mod\ 12)
對於正整數 n,整數 \small a_1, a_2, b_1, b_2,如果 a_1 \equiv a_2~(mod ~ n),b_1 \equiv b_2~(mod ~n)則我們可以得到如下性質:

a_1 + b_1 \equiv a_2 + b_2 ~ (mod ~n) \quad(1.9) a_1 \cdot b_1 \equiv a_2 \cdot b_2 ~ (mod ~n) \quad (1.10)

譬如,因爲 \small 9 \equiv 4 ~ (mod ~ 5),8 \equiv 3 ~ (mod ~ 5),則可以推出 \small 17 \equiv 7 ~ (mod ~ 5),72 \equiv 12 ~(mod ~5)

[證明] 同餘加法和乘法性質
因爲 a_1 \equiv a_2 ~(mod ~n),b_1 \equiv b_2 ~ (mod ~n),則說明存在整數 c 和 d,滿足 a_2 = a_1 + cn,b_2 = b_1 + dn,因此:a_2 + b_2 = a_1 + b_1 + (c+d) n 加法性質得證,同理可得: a_2b_2 = (a_1 + cn)(b_1 + dn)=a_1b_1 + (a_1d+b_1c+cdn)n 乘法性質得證。

另外,若 p 和 q 互質,且 a ≡ b (mod ~ p),a ≡ b (mod ~ q),則可推出:
a ≡ b (mod ~pq) \quad (1.11)

[證明] 因爲 a ≡ b (mod ~ p) \implies s|a-b,a ≡ b (mod ~ q) \implies q|a-b。而 p 和 q 互質,於是可得 pq|a-b,因此可得 a ≡ b (mod ~ pq)

此外,模的四則運算還有如下一些性質,證明也比較簡單,略去。

  • (a+b) ~mod ~ n = (a ~mod~ n + b ~ mod ~n) ~mod~ n
  • (a-b) ~mod ~ n = (a ~mod~ n - b ~ mod ~n) ~mod~ n
  • (a \cdot b) ~mod ~ n = (a ~mod~ n \cdot b ~ mod ~n) ~mod~ n
  • a^b ~mod~ n = ((a ~mod ~ n)^b) ~mod~ n

模逆元素: 對整數 a 和正整數 n,a 對模數 n 的模逆元素是指滿足以下條件的整數 b。ab \equiv 1(mod\ n) a 對 模數 n 的 模逆元素不一定存在,a 對 模數 n 的模逆元素存在的充分必要條件是 a 和 n 互質,這個在後面我們會有證明。若模逆元素存在,也不是唯一的。例如 a=3,n=4,則 a 對模數 n 的模逆元素爲 7 + 4k,即 7,11,15,...都是整數 3 對模數 4 的模逆元素。如果 a 和 n 不互質,如 a = 2,n = 4,則不存在模逆元素。

[推論2] 模逆元素存在的充分必要條件是整數 a 和 模數 n 互質。

[證明] 模逆元素存在性證明

  • 若 a 和 n 互質,則 gcd(a, n) = 1,由擴展歐幾里得算法知道,必然存在 x, y 使得 ax + ny = gcd(a, n) = 1,則模除 n 可得 ax ≡ 1(mod\ n),顯然 x 就是 a 對 模數 n 的模逆元素。事實上,x + kn(k \in \mathbb{Z}) 都是 a 對模 n 的模逆元素,取其中最小的就是 x。
  • 若 a 和 n 不互質,設 gcd(a, n) = g,則 ax + ny = g (1 < g <= n),則在模除 n 時,ax ≡ g 不會同餘爲 1,故此時不存在模逆元素。

1.7 唯一質數分解定理

[定理3] 唯一質數分解定理: 任何一個大於1的正整數 n 都可以 唯一分解 爲一組質數的乘積,其中 e_1,e_2... e_k 都是自然數(包括0)。比如 6000 可以唯一分解爲 \small 6000 = 2^4 \times 3 \times 5^3

n = 2^{e_1} \cdot 3^{e_2} \cdot 5^{e_3} ... = \prod_{k=1}^r p_k^{e_k}

[證明] 唯一質數分解定理

存在性證明

  • 使用反證法來證明,假設存在大於1的自然數不能寫成質數的乘積,把最小的那個稱爲 n。自然數可以根據其可除性(是否能表示成兩個不是自身的自然數的乘積)分成3類:質數、合數和 1。首先,按照定義,n大於1。其次,n 不是質數,因爲質數 p 可以寫成質數乘積:p = p^1,這與假設不相符合,因此n只能是合數。
  • 但每個合數都可以分解成兩個嚴格小於自身而大於 1 的自然數的積。設 n = a \cdot b(1 < a, b < n),由假設可知,a 和 b 都可以分解爲質數的乘積,因此 n 也可以分解爲質數的乘積,所以這與假設矛盾。由此證明所有大於 1 的自然數都能分解爲質數的乘積。

唯一性證明

  • 當 n=1 的時候,確實只有一種分解。
  • 假設對於自然數 n>1,存在兩種因式分解: n=p_1...p_m = q_1...q_k(p_1<=...<=p_m,q_1<=...<=q_k),其中 p 和 q 都是質數,我們要證明 p_1=q_1, ..., p_m=q_k。如果不相等,我們可以設 p_1 < q_1,從而 p_1 小於所有的 q。由於 p_1q_1 是質數,所以它們的最大公約數爲1,由歐幾里德算法可知存在整數 a 和 b 使得 ap_1 + bq_1 = 1。因此
    a p_1 q_2...q_k + b q_1 q_2...q_k = q_2 ... q_k (等式1)。由於 q_1...q_k = n,因此等式1左邊是 p_1 的整數倍,從而等式1右邊的 q_2...q_k 也必須是 p_1 的整數倍,因此必然有 p_1 = q_i(i > 1),而這與前面 p_1 小於所有的 q 矛盾。
  • 由對稱性,對 p_1 > q_1 這種情況可以得到類似結論,故可以證明 p_1 = q_1,同理可得 p_2 = q_2, ..., p_m=q_k,由此完成唯一性的證明。

由質數唯一分解定理可以得到一個推論:質數有無窮多個

[證明] 質數有無窮多個。

  • 用反證法。假設質數有限,爲 p_1, p_2, ..., p_k,最大質數爲 p_k,則令 N 爲所有質數之積+1,即 N = p_1p_2...p_k + 1,顯然 N > p_k,且 N 不能被已有的任何一個質數整除。

  • 由質數唯一分解定理知道,N 可以分解爲質數的乘積,但是它無法分解爲已有質數的乘積,那麼要麼 N 自己是質數;要麼 N 是合數,但是存在比 p_k 更大的質數分解因子。不管哪種情況,都與假設的最大質數爲 p_k 矛盾,故假設不成立,質數有無窮多個。

1.8 中國剩餘定理

[定理4] 中國剩餘定理(Chinese remainder theorem,CRT),最早見於《孫子算經》(中國南北朝數學著作,公元420-589年),叫物不知數問題,也叫韓信點兵問題。

有物不知其數,三三數之剩二,五五數之剩三,七七數之剩二。問物幾何?

翻譯過來就是已知一個一元線性同餘方程組求 x 的解:(S):\begin{cases} & x \equiv 3 \ (mod\ 2) \\ & x \equiv 5 \ (mod\ 3) \\ & x \equiv 7 \ (mod\ 2) \end{cases}

宋朝著名數學家秦九韶在他的著作中給出了物不知數問題的解法,明朝的數學家程大位甚至編了一個《孫子歌訣》:

三人同行七十希,五樹梅花廿一支,七子團圓正半月,除百零五便得知。

意思就是:將除以 3 的餘數 2 乘以 70,將除以 5 的餘數 3 乘以 21,將除以 7 的餘數 2 乘以 15,最終將這三個數相加得到 \small 2 \cdot 70 + 3 \cdot 21 + 2 \cdot 15 = 233。再將 233 除以 3,5,7 的最小公倍數 105 得到的餘數 \small 233 % 105 = 23,即爲符合要求的最小正整數,實際上,\small 233 + 105k(k \in \mathbb{Z}) 都符合要求。

物不知數問題解法本質

  • 從 5 和 7 的公倍數找到一個除以 3 餘 1 的 n_1 (如70),從 3 和 7 的公倍數中找一個除以 5 餘 1 的 n_2(如21),從 3 和 5 的公倍數找一個除以 7 餘 1 的 n_3 (如15),則 \small 2n_1+3n_2+2n_3=140+63+30=233,模除 105,得最小正整數解 23。
  • 因爲 n_1 除以3 餘1, n_2n_3 都是 3 的倍數,則由性質 1.9 知,\small n_1+n_2+n_3 = 106 滿足除以 3 餘 1,同理也可得它滿足除以 5 餘 1,除以 7 餘 1的條件。而前面找的是餘 1 的數,使用性質 1.9 和 1.10 可知,乘以餘數後 \small 2n_1+3n_2+2n_3 正好滿足要求,即除以 3 餘 2,除以 5 餘 3,除以 7 餘 2。爲什麼要先找餘 1 的數,然後再乘以餘數呢?這個可以在後面的通項公式求解知道其中緣由。

求解通項公式

中國剩餘定理相當於給出了以下的一元線性同餘方程組的有解的判定條件,並用構造法給出瞭解的具體形式。

(S): \begin{cases} & x \equiv a_1 \ (mod\ m_1) \\ & x \equiv a_2 \ (mod\ m_2) \\ & \vdots \\ & x \equiv a_n \ (mod\ m_n) \end{cases}

模數 m_1,m_2,...,m_n 兩兩互質,則對任意的整數:a_1,a_2,...,a_n,方程組 (S) 有解,且解可以由如下構造方法得到:

  • 1)設 M爲所有模數的乘積

M = m_1 \cdot m_2 \cdot ... m_n = \prod _{i=1}^n m_i

並設 M_i 是除 m_i 以外的其他 n−1 個模數的乘積。

M_i = M / m_i, ~ \forall i\in \{ 1, 2, ... n \}

  • 2)設 t_iM_i 對模數 m_i 的模逆元素,即

t_i M_i \equiv 1 ( mod ~ m_i), ~ \forall i \in \{ 1,2,...n\}

  • 3)則方程組 (S)的通解形式爲:

x = a_1t_1M_1 + a_2t_2M_2 + ... a_nt_nM_n + kM = kM + \sum _{i=1}^na_it_iM_i, ~ k \in \mathbb{Z}

  • 4)以物不知數問題爲例,則由 \small a_1 = 2, a_2 = 3, a_3 = 2,m_1 = 3, m_2 = 5, m_3 = 7\small M = 3 \cdot 5 \cdot 7 = 105,M_1 = M/m_1 = 35,同理得 \small M_2 = 21,M_3 = 15。求 \small M_1(35) 模除 \small m_1(3) 的最小的模逆元素,可得 \small t_1 = 2,同理得 \small t_2 = 1,t_3 = 1。故而解爲:

\small x = a_1t_1M_1 + a_2t_2M_2 + a_3t_3M_3 + kM
\small = 2 \times 2 \times 35 + 3 \times 1 \times 21 + 2 \times 1 \times 15 + k \times 105
\small = 233 + k \times 105, \quad k \in \mathbb{Z}

中國剩餘定理通項公式證明

[證明] 中國剩餘定理

  • 1) 先證明簡單的情況,假設只有兩個方程,\small m_1, m_2互質,求解 x。
    (S):\begin{cases} & x \equiv a_1 \ (mod\ m_1) \\ & x \equiv a_2 \ (mod\ m_2) \\ \end{cases}
    這裏我們構造一個解:
    x = a_1t_1m_2 + a_2t_2m_1 \quad (t_1m_2 \equiv 1 ~(mod ~ m_1),t_2m_1 \equiv 1 ~ (mod ~m_2))t_1 是 對模數 m_2 的模逆元素,t_2m_1 對模數 m_2 的模逆元素 (因爲 m_1 和 m_2 互質,則對應的模逆元素一定存在),則可以證明 x 是滿足條件的解,證明如下:
    x \equiv (a_1t_1m_2 + a_2t_2m_1) \equiv a_1t_1m_2 \equiv a_1 (mod ~ m_1)
    x \equiv (a_1t_1m_2 + a_2t_2m_1) \equiv a_2t_2m_1 \equiv a_2 (mod ~ m_2)

    當然,對於兩個方程的情況,我們也可以直接根據模運算的定義來求解,這個方式也會在後面 RSA 算法優化中用到。因爲 x \equiv a_2 (mod ~ m_2),於是可以令:
    x = km_2 + a_2 代入到第一個方程有:
    km_2 + a_2 \equiv a_1 ~ (mod ~ m_1) 根據性質 1.9,可得:
    a_1 - a_2 \equiv km_2 (mod ~ m_1) 因爲 m_1,m_2 互質,所以 m_2 對於 m_1 必然存在模逆元素 t_2,滿足 t_2m_2 \equiv 1~ (mod ~m_1),根據性質 1.10 可得:
    t_2(a_1 - a_2) \equiv kt_2m_2 \equiv k ~(mod ~ m_1) 由此可以得到 k = t_2(a_1-a_2) ~mod ~m_1,從而得到 x 在 [0, m_1m_2) 範圍內的唯一解是:
    x = a_2 + km_2 = a_2 + (t_2(a_1-a_2) ~ mod ~ m_1) \cdot m_2 \quad (1.12)

  • 2)推廣到 n 個方程組,對 \forall i \in \{1, 2, ..., n \},則對 \forall j \in \{1, 2, ..., n \},j \neq i,因爲 m_im_j 互質,則可得 gcd(m_i, m_j) = 1,且 m_iM_i 互質,則 gcd(m_i, M_i) = 1。因爲 gcd(m_i, M_i) = 1,由推論2 知,必然存在整數 t_i,滿足:t_iM_i \equiv 1(mod\ m_i),即 t_iM_im_i 的模逆元素。考察乘積 a_it_iM_i 可知:
    a_it_iM_i \equiv a_i (mod \ m_i)\forall j \in \{1, 2, ..., n\},j \neq i,a_it_iM_i \equiv 0 (mod \ m_j) ,故而 x = a_1t_1M_1 + a_2t_2M_2 + ... a_nt_nM_n 滿足:
    x = a_it_iM_i + \sum_{j \neq i} a_jt_jM_j \equiv a_i + \sum_{j \neq i}0 \equiv a_i(mod \ m_i) 所以可以得出 x 就是方程組 (S) 的一個解。

  • 3)另外,假設 x_1x_2 都是方程組的解,那麼:

    \forall i \in \{1, 2, ..., n\}, \quad x_1-x_2 \equiv 0 \ (mod\ m_i),而 m_1, m_2, ..., m_n 互質,從而可得 M\ |\ (x_1 -x_2)。即 兩個解必然相差 M 的整數倍,故而方程組通解爲:
    \{x = \sum_{i=1}^na_it_iM_i+ kM, \ k \in \mathbb{Z}\}

1.9 歐拉函數、歐拉定理和費馬定理

1.9.1 歐拉函數

歐拉函數定義:對正整數 n,歐拉函數 φ(n) 是指小於或等於 n 的正整數中與 n 互質的數的數目。比如 φ(1)=1,φ(8) = 4(因爲 1,3,5,7 都與 8 互質)。由唯一質數分解定理可知,正整數 n 可以分解爲質數乘積,據此可以得到歐拉函數的計算公式如下。

唯一質數分解:n = \prod_{k=1}^r p_k^{e_k}
歐拉函數計算公式:φ(n) = n \prod_{k=1}^r (1 - \frac {1}{p_k})

72 = 2^3 \times 3^2,故 φ(72) = 72 \times (1-1/2) \times (1-1/3)=24

歐拉函數計算公式推導:

  • 1)若 n 爲 1,顯然 φ(1) = 1
  • 2)若 n 爲質數,有性質 1.8 可知,每個小於 n 的數都與 n 是互質數。則 φ(p) = p - 1
  • 3)若 n 爲質數的冪,即 n = p^k (p爲質數,k爲大於等於1的整數),則因爲只有當一個數不包含質數 p,纔可能與 n 互質。而包含質數 p 的數一共有 p^{k-1} 個,故互質數目爲 p^k - p^{k-1}
    φ(n) = p^k - p^{k-1} = p^k(1 - \frac{1}{p})
  • 4)若 n 爲兩個互質數的乘積,即 n = p \cdot p (p, q 爲互質數)。則 φ(n) = φ(p) \cdot φ(q),例如 φ(56) = φ(8) \cdot φ(7) = 4 \cdot 6 = 24。在證明之前,可以看個特例,即 p 和 q 都是質數,則 p 和 q 肯定是互質數,這種情況下與 n 互質的數不能包含質因子 p 和 q,p 的倍數有 q 個,q 的 倍數有 p 個,這裏還多算了一個數 n,它即是 p 的倍數也是 q 的倍數,因此 1 到 n 中 p 和 q 的倍數的數一共有 p+q-1 個,則可以推導如下:

φ(n) = n - (p + q-1)
= pq - (p + q - 1) = (p-1)(q-1)
= p(1 - \frac{1}{p})q(1-\frac{1}{q})
= φ(p)φ(q)

若 p 和 q 互質,但是 p 和 q 不一定是質數。 則 gcd(p, q) = 1,設小於 p 的數中與 p 互質的正整數有 φ(p) 個,則這些數的集合爲 R = \{r_1, r_2, ..., r_{φ_p}\}。而小於 q 且與 q 互質的正整數有 φ(s)個,設這些數的集合爲 S = \{s_1, s_2, ..., s_{φ_q}\}。從 集合 R 和 S中分別選取一個數 r 和 s,構造方程組 x \equiv r(mod\ p) 以及 x \equiv s(mod \ q),由中國剩餘定理可知,則當 x < pq 時方程組存在且僅存在一個正整數解,即 x ~ mod ~ pq 有唯一解。且該方程組在 p 和 q相同的情況下,如果 r 和 s 不同,則解 x 也一定不同。下面證明解 x 滿足 gcd(x, pq) = 1

因爲 x\ \%\ p = r,由歐幾里得算法可得 gcd(x, p) = gcd(p, r),當 gcd(p, r) = 1 時,則可得 gcd(x, p) = 1,同理,gcd(x, q) = 1。由 gcd(x, p) =1gcd(x, q) = 1,且 p、q 互質,可得 gcd(x, pq) = 1,即 x 是滿足歐拉函數的一個數。 而 <r, s> 這樣的數對一共有 φ(p) φ(q) 個,而 x 一共有 φ(pq)個,故而得證 φ(pq) = φ(p)φ(q)

看一個實際的例子,設 x= 15 = p \cdot q = 3 \cdot 5,則對於 x ~mod~ 3 = a, x ~mod~ 5 = b,(a, b) 和 x 存在如下一一對應關係。即若 p 和 q 爲互質數,則對於任意的數對 <a, b> \in \Bbb Z_p \times \Bbb Z_q,存在唯一的 x \in \Bbb Z_{n}(其中 \Bbb Z_n 表示整數集合 \{0, 1, ..., n-1\})。

(a, b) x (a, b) x (a, b) x
(0, 0) 0 (1, 0) 10 (2, 0) 5
(0, 1) 6 (1, 1) 1 (2, 1) 11
(0, 2) 12 (1, 2) 7 (2, 2) 2
(0, 3) 3 (1, 3) 13 (2, 3) 8
(0, 4) 9 (1, 4) 4 (2, 4) 14
    1. 綜上,可以得到通項公式:

n = p_1^{e_1} \cdot p_2^{e_2} \cdot ... p_r^{e_r}

φ(n) = φ(p_1^{e_1} \cdot p_2^{e_2} \cdot ... p_r^{e_r})
= φ(p_1^{e_1}) \cdot φ(p_2^{e_2}) \cdot ... φ(p_r^{e_r}) (由4可得)
= p_1^{e_1} \cdot p_2^{e_2} ... p_r^{e_r}\cdot(1 - \frac{1} {p_1})\cdot(1-\frac{1}{p_2})...(1-\frac{1}{p_r}) (由3可得)
= n\cdot (1 - \frac{1}{p_1})\cdot(1-\frac{1}{p_2})...(1-\frac{1}{p_r}) (由 n 分解式可得)

計算歐拉函數可以使用 python 的這個模塊: eulerlib,一個示例代碼如下:

import eulerlib
e = eulerlib.numtheory.Divisors(10000)
e.phi(21)

1.9.2 歐拉定理

歐拉定理基於歐拉函數而來,它是 RSA 算法的核心。內容如下:
歐拉定理: 如果兩個正整數 a 和 n 互質,則 n 的歐拉函數 φ(n) 滿足下面的條件:

a ^ {φ(n)} \equiv 1 (mod\ n)

基於前面的知識,我們來證明下歐拉定理。

[證明] 歐拉定理

  • 根據歐拉函數定義,小於 n 且與 n 互質的數有 φ(n) 個,設這些數的集合爲 U = u_1, u_2, ..., u_{φ(n)}。顯然對於 U 中的任意數 u_i,都有 u_i ~ mod ~ n = u_i。若一個正整數 a 與 n 互質,且 a 比 n 大,同樣滿足 a \ mod \ n 與 n 互質,即 (a ~ mod ~ n) \in U。證明如下:設 a = kn + r(k>=1, 0<r<n),由於 kn 不會與 n 互質,故 r 必須和 n 互質,而 r < n,故 r \in U,即 (a ~ mod ~ n) \in U。因此得到一個結論,只要正整數 a 與 n 互質,則有 (a ~mod ~ n) \in U

  • 接着我們可以證明若 a 與 n 互質,且 u 與 n 互質,則 au % n 與 n 互質,繼而可得到 (au ~ mod ~ n) \in U。這是爲什麼呢?因爲根據唯一質數分解定理,a 和 u 都可以分解爲質數乘積,如 \small a = \prod_{i=1}^s p_i^{e_i},而 \small u = \prod_{i=1}^t q_i^{f_i},同理,n也可以分解爲質因數乘積 \small n=\prod_{i=1}^m r_i^{g_i}。而由 a 和 u都和 n 互質,則 a 和 n 的質因子沒有交集,且 u 和 n 的質因子沒有交集,則 au 的質因子與 n 沒有交集,故而 au 和 n 互質,由前面的結論,可得到 (au ~ mod ~ n) \in U

  • 將集合 U 中的數都乘以 a,可以得到新的集合 AU,即 AU = \{a u_1, a u_2, ..., a u_{φ(n)}\},由前面分析可知 AU 中的每個數都與 n 互質。所以,對於任意的 u_i \neq u_j(i,~ j \in U,i \neq j),可得到 au_i ~ mod ~ n \neq au_j ~ mod ~ n,即 AU 中每個數除以 n 的餘數各不相同。這個用反證法不難證明,若存在 u_i \neq u_j(i,~ j \in U,i \neq j),滿足 au_i ~mod~ n = au_j ~mod ~n,則可以設 au_i = sn + r, au_j = tn + r,故可得 a(u_i - u_j) = (s-t)n,而 a 與 n 互質,則要滿足條件,只能 u_i - u_j 是 n 的倍數,而我們知道 |u_i - u_j| < n,與假設矛盾。由 AU 中每個數與 n 互質,且它們除以 n 的餘數與 n 互質且各不相同,故而 AU 中每個數除以 n 得到的餘數就是集合 U 中的那 φ(n) 個數,當然順序不一定是一一對應。因此 AU ~mod~ n = U

  • 有了上一步的結論,再加上模除的性質,就可以證明歐拉定理了:
    au_1 \cdot au_2...au_{φ(n)} \equiv u_1 \cdot u_2...u_{φ(n)}(mod ~n)
    a^{φ(n)}\cdot u_1 \cdot u_2...u_{φ(n)} \equiv u_1 \cdot u_2...u_{φ(n)}(mod ~ n)
    a^{φ(n)} \equiv 1(mod ~ n)

1.9.3 費馬小定理

費馬小定理:給定整數a和質數p,若a不是p的倍數,那麼:
a^{p-1} \equiv 1(mod ~ p)

費馬小定理可以看做是歐拉定理的特例。因爲當 p 爲 質數時,a 不是 p的倍數,則 a與p互質,且 p 的歐拉函數 φ(p) = p-1,代入歐拉定理即可得證。

2 RSA算法原理

RSA算法基於歐拉定理而來,如果只是爲了證明RSA算法原理,只要明白歐拉定理即可。當然,歐拉定理的證明涉及數論中許多經典的定理,有興趣的同學可以深入瞭解證明過程。話說回來,即便不看歐拉定理的證明過程,只要明白歐拉定理內容,看懂RSA算法原理也是沒有問題的。

2.1 RSA算法

1)生成兩個隨機的不相等的大質數 p 和 q,它們的積 n = pq 的二進制位數就是密鑰的位數,通常設置的位數有 1024,2048,3072,4096等。

[知識點] 大質數生成算法

如何生成這兩個大質數,這是個值得研究的問題。首先可以想到的是生成質數序列,從中隨機選取2個大質數即可。一種改進的方法是: 除了 2 外的偶數肯定不是質數,而奇數可能是質數,可能不是,那就可以跳過2與3的倍數,即對於 6n,6n+1, 6n+2, 6n+3, 6n+4, 6n+5,我們只需要判斷 6n+1 與 6n+5 是否是質數即可。而判斷某個數m是否是質數,最基本的方法就是對 2 到 m-1 之間的數除 m,如果有一個數能夠整除 m,則 m 就不是質數。判斷 m 是否是質數還可以進一步改進,只需要對 2 到 \small \sqrt m 之間的數除 m 就可以。繼續優化,其實只用 2 到 \small \sqrt m之間的質數去除即可。上面生成大質數是很基礎的方法,效率比較低,一般會採用更快的方式,比如隨機生成一個 nbits 位的奇數 p,然後從 p 開始遍歷之後的奇數,並通過 Miller–Rabin 質數判定算法對遍歷到的數字進行質數判定,如果是質數,即可返回(當然,該算法有一定的誤判率,不過在判定次數設置夠大的情況下,誤判率基本可以忽略)。

2)計算 p 和 q 的積 n=pq,以及歐拉函數 φ(n) = (p-1)(q-1)

3)選擇一個整數 e,1< e < φ(n),且 gcd(e, φ(n)) = 1,即 eφ(n) 互質,在 openssl 中 e 固定爲 65537。

[知識點]費馬數
對於e,通常可選 3, 5, 17, 257 和 65537。因爲它們都是質數,且二進制中只有2位是1,可以加快 d 的計算。這幾個e的可選值實際是費馬數的前5個數,費馬數定義是 F_x = 2^{2^x} + 1F_0F_4 都是質數,但是從 F_5 開始就不是質數了。例如 F_5 = 4294967297 = 641×6700417。實際應用中通常選擇 F_4 = 65537 作爲 e。

  1. 計算 e 對於 φ(n) 的模反元素 d。即找到整數d,1 < d < φ,且滿足 \small ed \equiv 1~(mod ~φ(n))

ed ≡ 1~(mod~ φ(n))
\implies ed - 1 = kφ(n)
\implies ed + φ(n)k'= 1

根據擴展歐幾里得算法,e 和 φ(n) 爲已知量,可以求得一組解 d, k',其中 d 就是我們需要找的模反元素。

5)n 和 e 封裝爲公鑰,n 和 d 封裝爲私鑰。

  • n:通常稱爲 modulus。
  • e:通常稱爲 public exponent。
  • d:通常稱爲 secret exponent。

6)定義好了公私鑰,則對信息 m(0 <= m < n),加密和解密函數如下:
c = RsaPublic(m) = m^e ~ mod ~ n (公鑰 n, e 加密)
m =RsaPrivate(c) = c^d ~ mod ~ n (私鑰 n, d 解密)

需要證明:
m = RsaPrivate(RsaPublic(m)) (1)
m = RsaPublic(RsaPrivate(m)) (2)

經過分析可以發現這兩個式子證明是一樣的,這裏證明(1)即可。(1)常用於客戶端公鑰加密數據然後服務端私鑰解密獲取原始數據,而(2)則常用於服務端私鑰加密簽名數據,客戶端公鑰解密獲取簽名數據並校驗簽名。

2.2 實例分析

在證明之前,先簡單驗證下。假定我們選擇 p=13, q=15,e = 17,則

  • n = 13\times15 = 195
  • φ(n) = (13-1)(15-1) = 168
  • 17d + 168y = 1,可以求得一個 d=89, k=-9

假定我們原始數據 m=16,則加密後數據爲 61,對 61 解密,可以得到原始數據 16。

  • c = m^e ~ \% n = 16 ^{17} ~ \% ~ 195 = 61
  • m = c^d ~ \% ~ n = 61^{89} ~ \% ~195 =16

2.3 算法正確性證明

由上一節可知,我們要證明的是:
((m^e ~mod ~ n) ^ d) ~mod~ n = m (0 <= m < n)
由模運算的冪性質,等式左邊: ((m^e ~ mod ~ n) ^ d) ~mod ~n= m^{ed} ~mod~ n,即我們要證明的是:
m^{ed} ~ mod ~ n = m \iff m^{ed} \equiv m (mod ~ n)

  • 由前面RSA算法構造條件知道 ed \equiv 1(mod ~ φ(n)),據此可設 ed = 1 + kφ(n)

  • 先證明 m 和 n 互質這種特殊情況。因爲 m 和 n 互質,由歐拉定理有 m^{φ(n)} \equiv 1(mod ~n)。由此推導如下:
    m^{ed} \equiv m^{1 + kφ(n)}
    \equiv (m \cdot ({m^{φ(n)}})^k)
    \equiv (m \cdot 1^k) (由歐拉定理和模運算的乘法結合律)
    \equiv m(mod ~n)

  • 下面證明的是 m 和 n 不互質的情況,因爲 n = pq,且 p 和 q都是質數,則 m 與 n 不互質只能是 p 或者 q 的倍數,不能同時爲 p 和 q的倍數,因爲 m < n。假設 m 是 q 的倍數,即 q|m,則 m \equiv 0 ( mod ~ q)。可得 m^{1 + kφ(n)} \equiv m \equiv 0 ~(mod ~ q)

    而因爲 p 是質數,m 又不是 p 的倍數,故 p 和 m 互質。由費馬小定理,可得到 :
    m^{p-1} \equiv 1 (mod ~ p)
    \implies m^{(p-1)(q-1)} \equiv 1^{q-1} \equiv 1 (mod ~ p) (兩邊 q-1 的冪次)
    \implies m^{φ(n)} \equiv 1 (mod ~ p) (因爲 \small(φ(n) = φ(pq) = (p-1)(q-1))
    \implies m^{kφ(n)} \equiv 1^k \equiv 1(mod ~ p) (兩邊 k 次冪)
    \implies m^{1+kφ(n)} \equiv m(mod ~ p) (兩邊乘以 m)

    m^{1+kφ(n)} \equiv m(mod ~ p) 以及 m^{1+kφ(n)} \equiv m(mod ~ q) ,從而可得 m^{1+kφ(n)} \equiv m(mod ~ pq),因爲 n=pq,故 m^{1+kφ(n)} \equiv m(mod ~ n) ,因爲 ed = 1 + kφ(n),根據性質 1.11,故而得證 m^{ed} \equiv m(mod ~ n)

  • 注意: 雖然定義中 0 <= m < n,但是實際上對於 m = 0,1,n-1 加密並沒有效果,很容易從加密過程知道,0,1,n-1 加密後都是它們自身。

2.4 RSA Key Pair的生成方法及代碼實現

RSA的Key Pair的生成方法的僞代碼如下:

INPUT: 模數 (即前面提到的N,N=pq) 的二進制位數 k
OUTPUT: RSA的密鑰對 ((N,e),d),其中 N 是 模數, N=pq,其二進制長度不超過 k bit。 e 是選擇的 public exponent,d 是 secret exponent。

\small Select ~ a ~ value ~ of ~ e ~ from ~ 3,5,17,257,65537
\small repeat
\small \quad p \leftarrow genprime(k/2)
\small until ~ (p~ mod ~ e) \neq 1
\small repeat
\small \quad q \leftarrow genprime(k - k/2)
\small until ~ (q ~ mod ~ e) \neq 1
\small N \leftarrow pq
\small L \leftarrow (p-1)(q-1)
\small d \leftarrow modinv(e, L)
\small return ~ (N,e,d)

1)首先就是找兩個位數爲 k/2 的大於0的大質數。在前面提到過,最樸素的方法是遍歷正整數集,對於數 n,用 \small2, 3, ..., \sqrt n 去除 n,但是這個方法的時間複雜度爲 \small \Theta(\sqrt n),這是正整數 n 的長度的冪(因爲 n 可以表示爲 b 位的二進制數,則 \small n = \lceil lg(n+1) \rceil,故而 \small \sqrt n = \Theta(2^{b/2}))。這個複雜度是比較高的,而我們不用遍歷所有的整數,完全可以隨機找一個大整數,然後判斷該數是不是質數即可。令人高興的是,判斷一個數是不是質數比對一個數進行質數因子分解要容易很多。

由費馬小定理知道,如果 n 是 質數,則對於任意的數 \small 1 <= a <= n-1 ,都滿足
\small a^{n-1} \equiv 1(mod~ n)(2.4.1)
則可以知道如果對於 \small 1, 2, ..., n-1\ 中如果有一個數 a 和 n 不滿足 2.4.1,則 n 一定是合數。當然我們在實際中也不會將所有小於 n 的正整數都遍歷來計算一遍,通常會隨機選取一個 a 作爲基數,比如選擇 a=2,則滿足費馬小定理而又不是質數的 n ,我們稱 n 爲基於 a 的一個僞質數。比如 341 滿足 \small 2^{(341-1)} \equiv 1(mod ~ 341),但是 341 是一個合數。如果將基數a換成3,那也不能保證,對於 \small \forall a \in \{1, 2, ...n-1\},總有些合數(這些數也被稱爲 Carmichael數)滿足2.4.1,如 561(a=2 滿足),1105(a=2和a=3都滿足)等。

爲此,我們可以使用 Miller-Rabin 算法來解決判斷質數問題。其主要思想就是選取 s 個 a 的隨機數,然後判斷奇數 n 是否是質數(偶數可以直接排除),如果其中任意一個 a 對應的 WITNESS(a, n) 爲 true,則表示 n 肯定是合數。如果 s 次測試都通過,則可以近似判定 n 是 質數(誤判的概率最大爲 \small 1/2^s ,一般實際應用中選s=50就可以了)。

INPUT: (n, s),n > 2,n 是奇數,s 是測試次數
OUTPUT: COMOSITE 表示爲合數,PRIME 則表示極可能是質數

\small MILLER-RABIN(n, s)
\small \quad for ~ j ~ 1 \leftarrow to ~ s
\small \quad \quad do ~ a \leftarrow RANDOM(1, n-1)
\small \quad \quad \quad if ~ WITNESS(a, n)
\small \quad \quad \quad \quad then ~ return ~ COMPOSITE // 肯定不是質數
\small \quad return ~ PRIME // 極可能是質數

INPUT: (a, n) 其中 a 爲隨機選擇的基數,n爲待判定的奇數
OUTPUT: TRUE 表示肯定是合數, FALSE 表示可能是質數

\small WITNESS(a, n)
\small \quad let ~ n-1 = 2^tu, where ~ t >=1 ~ and ~ u ~ is ~ odd
\small \quad x_0 = a^u ~ mod ~ n
\small \quad for ~ i \leftarrow 1~ to ~ t
\small \quad \quad do ~ x_i \leftarrow x_{i-1}^2 ~ mod ~ n
\small \quad \quad \quad if ~ x_i = 1 ~ and ~ x_{i-1} \neq 1 ~ and ~ x_{i-1} \neq n-1
\small \quad \quad \quad \quad then ~ return ~ TRUE
\small \quad if ~ x_i \neq 1
\small \quad \quad then ~ return ~ TRUE
\small \quad return ~ FALSE

而 WITNESS 就是根據 2.4.1 來判斷 n 是否通過檢測,不過做了一些優化,因爲 n 是奇數,所以可以將 n-1分解爲 \small n-1 = 2^tu(t>=1, u爲奇數),設 \small x_0 = a^u ~ mod ~ n,則對 x 的結果平方 t 次,即可計算出 \small a^{n-1} ~ mod ~ n。所計算的序列 x_0, x_1, ...x_t 滿足 \small x_i \equiv a^{2^i}u(mod ~ n)(i = 0, 1, ...t)。 如果前一個值 \small x_{i-1} 不等於 1 或者 n-1,但是當前值 \small x_{i} = 1,則 n 一定是合數。證明如下:

\small WITNESS 正確性證明:

  • 首先可以證明這麼一個結論:若 a ,n爲正整數,且 n 爲質數,\small a^2 \equiv 1 (mod ~n),則有 \small a \equiv \pm1(mod ~ n)
  • 這是因爲
    \small a^2 \equiv 1(mod ~n) \implies a^2 - 1 \equiv 0(mod ~n)
    \small \quad \implies (a+1)(a-1) \equiv 0(mod ~ n)
    \small \quad \implies a + 1 \equiv 0(mod ~ n) 或者 \small a - 1 \equiv 0(mod ~ n)
    \small \quad \implies a \equiv \pm1(mod ~ n)(因爲 a > 0)
  • 下面就很容易證明 WITNESS了,因爲 \small n-1 = a^{2^i}u,如果 n 是質數的話,則由 \small x_i = 1,可以推出 x_{i-1} 一定是1 或者 n-1,否則 n一定是合數。

2)第二步就是計算 N=pq,然後計算歐拉函數 L,最後根據 e 和 歐拉函數 L 計算出 e 模除 L 的模擬元素 d 即可。

3)基於該算法,我寫了個 RSA 密鑰生成的 python 實現供大家參考,見 shishujuan:rsa-algrithm

2.5 提高 RSA 加解密效率

由前面分析知道 RSA 加解密效率嚴重依賴求冪和求模運算效率,爲了減少大整數的求冪和求模的運算,可以對 RSA 模冪算法(modular exponentiation)進行一些優化。主要包括兩方面,一是對模冪運算本身進行優化,提升運算效率。而是對RSA算法進行優化,基於中國剩餘定理來提升RSA加解密效率。

加密:c = m^e ~ mod ~ n
解密:m = c^d ~ mod ~ n

2.5.1 提升模冪運算性能

模冪運算-樸素法

樸素法求模冪就是直接求冪然後模除,如下:

def pow_simple(a, e, n):
    """
    樸素法模冪運算:a^e % n
    """
    ret = 1
    for _ in xrange(e):
        ret *= a
    return ret % n

當冪 b 很大時,這個方法的模冪運算就很慢,在我的機器上 a=5, e=102400, n = 13284 大概需要1秒左右的時間。我們知道 RSA 私鑰中的 d 的值可是遠大於 102400 的,如果這麼慢顯然效率上會有問題。

一種簡單的優化方法基於下面這個性質,可以很方便的迭代實現,而且每次計算會將參數減小,優化後的模冪運算效率大概提升了100倍左右。

a \equiv c(mod ~ n) \implies ab \equiv bc(mod ~n)
\implies ab ~mod~n = (b(a ~ mod ~ n))(mod ~ n)

代碼如下:

def pow_simple_optimized(a, e, n):
    """
    樸素法模冪運算優化:基於 a ≡ c(mod n) => ab ≡ bc(mod n),
    即 ab mod n = (b*(a mod n)) mod m
    """
    ret = 1
    c = a % n
    for _ in xrange(e):
        ret = (ret * c) % n
    return ret

模冪運算-二進制法

雖然優化過的樸素方法的效率已經有了大幅提升,但是對於RSA私鑰的超大的d值,仍然性能堪憂。比如當冪級數 e 大小爲億級別時,優化過的樸素方法在我的機器需要接近10秒的時間求模。一種廣爲應用的模冪方法就是二進制法,基於位運算效率會有驚人的提升,e爲億級別時模冪運算也只需要零點幾毫秒。

對於一個n位的冪級數 e,我們可以寫成如下二進制形式:
e = e_{n-1}e_{n-2}...e_1e_0 = \sum_{i=0}^{n-1} e_i2^i (其中 e_{n-1} 是最高位,e_0是最低位)
\implies a^e = a^{\sum_{i=0}^{n-1}e_i2^i} = \prod_{i=0}^{n-1}(a^{2^i})^{e_i}
\implies c \equiv \prod_{i=0}^{n-1}(a^{2^i})^{e_i} (~mod ~ n)

從最低位 e_0 開始遍歷 e_i(i=0, 1, ...n-1),如果 e_i = 0,則不影響模除結果,設置base值,繼續處理下一位即可。在掃描到 e_i 時,有 base = a^{2^i} ~mod~ n,根據模除的乘法結合律 (a * b) ~mod ~n = ((a ~mod~ n) * (b ~mod~ n)) ~mod ~ n,可以知道下面算法是正確的。Python的內建函數 pow(x, y, z) 就是使用了類似的算法,因此在模冪運算時,使用 pow(x, y, z) 函數比 x**y ~\%~ z (直接冪運算跟優化過的樸素法性能相當)性能要高出幾個數量級。

def pow_binary(a, e, n):
    """
    right-to-left binary method:基於位運算模冪運算優化。
    """
    number = 1
    base = a
    while e:
        if e & 1:
            number = number * base % n
        e >>= 1
        base = base * base % n
    return number

完整的代碼見 pow.py

2.5.2 分解大整數提升模冪運算效率

從前面分析知道,通常使用過程中公鑰的冪級數 e 的選取比較小,而且選取的是費馬數,只有2位是1,其他位是0。而私鑰的冪級數 d 很大,它的位數與模數 N 差不多,且有接近一半位是1。爲了提高私鑰 d 在模冪運算中性能,可以基於中國剩餘定理(CRT)進行相關優化。

使用中國剩餘定理提升私鑰模冪運算效率(其中 n^{-1} _m 代表 n 對模數 m 的模逆元素,即 n^{-1}_m n \equiv 1(mod ~m))
dP = e^{-1} ~ mod ~ (p-1) = d ~mod~ (p-1)
dQ = e^{-1} ~mod~ (q-1) = d ~mod ~ (q-1)
m1 = c^{dP} ~ mod ~ p
m2 = c^{dQ} ~ mod ~ q
qInv = q^{-1} _p
h = qInv \cdot (m1 - m2) ~mod~ p
m = m2 + h \cdot q

這裏多出來的 dP,dQ,qInv的值在使用 openssl 生成的 rsa 密鑰中也有,其目的就是爲了加速私鑰模冪運算。以 2.2 中的例子爲例,直接求 m 可得 \small m = c^d ~mod ~n = 61^{89} ~mod~ 195 = 16,而使用中國剩餘定理優化後求解流程如下,亦可得到正確的結果 m = 16。

  • dP = d ~ mod~ (p-1) = 89 ~ mod~(13-1) = 5
  • dQ = d ~ mod ~ (q-1) = 89 ~ mod ~ (15-1) = 5
  • m1 = c^{dP} ~ mod ~ p = 61^5 ~ mod ~ 13 = 3
  • m2 = c^{dQ} ~ mod ~ q = 61^5 ~ mod ~ 15 = 1
  • qInv = q^{-1}_p = 15^{-1}_{13} = 7
  • h = qInv \cdot (m1-m2) ~ mod ~ p = 7 \cdot(3-1) ~ mod ~ 13 = 1
  • m = m2 + h \cdot q = 1 + 1 \cdot 15 = 16

[證明]

  • 由中國剩餘定理知道,對於 x ~ mod ~ p = x_1, x ~ mod ~ q = x_2(0 <= x_1 < p, 0 <= x_2 < q) 這個方程組在 p 和 q互質時,則必然存在一個唯一解 0 =< x < pq。因爲 n=pq, 因此對於任意的 0 <= x < n,必然存在唯一的數對 <x_1, x_2> 使之滿足方程組。我們要計算的是 m = c^d ~ mod ~ n=c^d ~mod~ pq,如果我們知道了 <c^d ~ mod ~ p, ~ c^d ~ mod ~ q>,則由中國剩餘定理知,必然存在一個唯一的值 c^d ~mod~ n 與之對應。

  • 由 1.12 中中國剩餘定理的解的格式可知,若已知
    c^d ~ mod ~ p = m_1,c^d ~ mod ~ q = m_2,n=pq,則有
    x = c^d ~ mod ~n = m_2 + h \cdot q
    h = (q^{-1}_p(m_1 - m_2)) ~ mod ~ p

  • 可以發現這與前面計算 m_1 並不是直接用的 c^d ~ mod ~ p,而是 c^{d ~mod ~(p-1)} ~mod~ p,這是爲何呢?爲什麼會有 c^d \equiv c^{d ~ mod ~ (p-1)} ~ (mod ~p)
    這個可以用歐拉定理證明:設 d = kφ(p) + d ~mod~ φ(p),則有
    c^d = c^{kφ(p) + d ~mod~ φ(p)} = (c^{φ(p)})^k \cdot c^{d ~mod ~φ(p)} 由歐拉定理知道
    c^{φ(p)} \equiv 1(mod ~ p) 於是有:
    c^d \equiv 1^k \cdot c^{d ~mod ~φ(p)} \equiv c^{d ~mod ~φ(p)} \equiv c^{d ~mod ~(p-1)} (mod ~ p) 得證。

使用剩餘定理優化的python實現參見 rsa.py 中的 decrypt_crt 函數。在我電腦上測試發現,優化後效率提升5倍左右。

2.6 如何破解 RSA?

從RSA加解密原理可知,如果要破解RSA,那就是知道 d 即可,由 \small ed \equiv 1(mod ~ n) 可知,要知道 d,就需要 e 和 φ(n),e 是公開的,而 \small φ(n) = (p-1)(q-1)是未知的,想知道 φ(n) 就要知道 p 和 q 這兩個大質數的值。我們知道 n 也是公開的,\small n=pq,所以只要能將 n 進行質數因式分解即可破解RSA算法。如果 N 是個小整數,那很好辦,比如 N = 25777,知道 \small \sqrt[2]{25777} < 161 ,則可以從 161 開始找比它小的質數,然後判斷是不是可以整除即可,很快可以得到 \small 25777 = 149 * 173

不過不要擔心,大整數的質數因式分解還是比較難的。雖然除了暴力破解外,還有一些效率比較高的整數因式分解方法,如 二次篩分算法普通數域篩選法(GNFS) 等,當你的RSA密鑰位數在4096位以上,使用常規運算能力的電腦還是較難破解的。當然不排除某一天,數論研究出現了新的成果,若有一種通項公式可以分解大整數的話,那現有基於 RSA 的加密體系都會崩塌。此外,RSA 加密用的 e 不能太小,否則有潛在的風險,實際應用中常用 65537。

如果已知 N,e,d,則可以很高效地對 N 實現因式分解,原理就不贅述了,實現代碼見 factor.py

在實際應用如HTTPS中,RSA 密鑰因爲性能原因以及沒有前向安全性,通常並不直接用來加密數據,而是隻用於在密鑰交換時用於簽名以認證身份,加密通常會基於 ECDHE 等算法協商一個密鑰,然後再基於對稱加密算法如 AES 等對數據加密傳輸。

3 RSA 密鑰格式實例分析

本節對使用 openssl 等工具生成的 RSA 密鑰格式進行簡單分析,首先,使用 openssl 生成一對 RSA 密鑰,如下:

# openssl genrsa -out rsa_demo.key 2048
# openssl rsa -in rsa_demo.key -pubout -out rsa_demo.pub

示例的私鑰文件 rsa_demo.key,公鑰文件 rsa_demo.pub

最常用的 RSA 密鑰的模式是 PKCS#1 中規定的模式,即公鑰包括 N, e,而私鑰包括 N, e, d, p, q, dP, dQ, qInv。查看公私鑰文件內容,可以看到採用的是 DER(Distinguished Encoding Rules) 編碼的,公鑰解碼後內容如下:

# grep -v '\-\-\-\-\-' rsa_demo.pub | tr -d '\n'|base64 -D|hexdump
0000000 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01
0000010 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01
0000020 00 bd e4 43 1a d0 02 1e e6 12 34 14 91 84 4d 65
......
0000120 b1 02 03 01 00 01                              
0000126

DER 編碼是一種type-length-value編碼,即包括:對象類型,數據長度域,數據域。示例的 RSA 公鑰中, 0x30 是 Sequence 類型,0x03 是 Bit String 類型,而 0x02Integer 類型,0x04是字符串類型,0x06Object ID類型,0x05 00表示NULL。長度如果>=128,則會先跟一個 0x810x82(0x81表示後面一個字節爲長度,0x82表示後面兩個字節爲長度),然後是數據長度的值,沒超過則就是長度值。整個結構是一個嵌套結構,公鑰的 n 和 e 兩個數值位於 BIT STRING 這塊數據中,可以分析知道從 00000020開始的 00bde4...b1是 n,而從 0000123開始的 010001 是 e,即 65537。另外,Object ID是標識密鑰的元信息的地方,對應的是 2a 86 48 86 f7 0d 01 01 01 這 9 個字節,這個代表什麼含義呢?其實轉義過來是 1.2.840.113549.1.1.1,這個 OID 表示的是 RSA 加密系統的公鑰。私鑰格式類似,這裏不再贅述,openssl 生成的私鑰中包括了 p,q,dP,dQ,n,e,d,qInv數據。

如果不想了解公私鑰的格式,直接通過 openssl 的工具即可解析出公私鑰的內容,容易驗證,這些值正是基於前面的 RSA 原理計算得來的。

# openssl rsa -in rsa_demo.key -noout -text
Private-Key: (2048 bit)
modulus: # N
    00:bd:e4:43:1a:d0:02:1e:e6:12:34:14:91:84:4d:
    ...
publicExponent: 65537 (0x10001) # e
privateExponent: # d
    76:bb:8d:41:ec:a2:06:d3:f0:b9:e3:ca:81:21:2b:
    ...
prime1: # p
    00:fa:99:34:b8:b3:97:dc:09:37:29:dd:df:de:86:
    ...
prime2: # q
    00:c1:fc:13:f7:84:dd:f4:b5:05:2d:89:b9:a1:50:
    ...
exponent1: # dP
    00:c9:7d:61:cc:98:6a:23:bb:2d:25:76:86:47:cf:
    ...
exponent2: # dQ
    00:99:e8:43:7b:45:ea:c8:45:7b:57:37:07:95:ea:
    ...
coefficient: # qInv
    0c:53:5f:0c:a6:7b:33:69:32:b6:b9:65:f8:31:5f:
    ...
    
# openssl rsa -pubin -in rsa_demo.pub -noout -text
Public-Key: (2048 bit)
Modulus: # N
    00:bd:e4:43:1a:d0:02:1e:e6:12:34:14:91:84:4d:
    ...
Exponent: 65537 (0x10001) # e

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章