Miller-Rabin 算法與 RSA 算法

jopen 9年前發布 | 17K 次閱讀 算法

Miller-Rabin算法是目前主流的基于概率的素數測試算法)RSA加密算法是計算機世界中最重要的一種非對稱加密算法。本文從SICP 習題1.28出發,主要介紹兩種算法的數學原理以及Scheme的簡單實現,代碼保存在 Gist

1. Miller-Rabin 素數檢驗算法

 一個數是素數(也叫質數),當且僅當它的約數只有1和它本身。為了檢驗 n 是否為素數,最直接的方法就是逐一檢驗 [2,n?1] 能否整除 n

(define (prime-iter n i)
  (cond ((= i n) #t)
        ((= (remainder n i) 0) #f)
        (else (prime-iter n (+ i 1)))))

(define (prime? n)
  (prime-iter n 2))

但是當數字較大的時候,這一算法的效率非常低下。于是就有了基于費馬小定理的概率算法:

費馬小定理:如果 p 是素數,a 是小于 p 的正整數,那么:ap?1 mod p=1

所有素數都滿足費馬小定理的條件,因此從小于p的整數中隨機抽取a,只要有一個a不滿足上述條件則p為合數,抽取次數越多,全部通過費馬小定理之后我們準確判定p為質數的概率越高:

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp) 
           (remainder 
             (square (expmod base (/ exp 2) m)) 
             m))
        (else (remainder 
                (* base (expmod base (- exp 1) m)) 
                m))))

(define (fermat-test n)
  (define (try-it a)
    (= (expmod a n n) a))
  (try-it (+ 1 (random (- n 1)))))

(define (fast-prime? n times)
  (cond ((= times 0) #t)
        ((fermat-test n)
             (fast-prime? n (- times 1)))
        (else #f)))

然而費馬小定理只是質數的必要而非充分條件,有些數字每一個比它小的整數都能通過費馬測試,但卻是合數,這種數字稱為卡邁克爾數(Carmichael numbers)。最小的卡邁克爾數是 561[1,1017] 之間有585,355 個卡邁克爾數,這使得費馬測試變得不可靠,于是就有了Miller-Rabin算法。

Miller-Rabin算法基于如下定理:

p 是素數,a 是小于 p 的正整數,且 a2 mod p=1,那么要么 a=1,要么 a=p?1

證明: a2 mod p=1,則說明 a2?1=(a+1)(a?1) 能夠整除 p,由于 p 是素數,能夠整除它的只有 1p 本身,因此 a 的解只能是 a=1a=p?1

基于上面的定理我們來檢驗最小的卡邁克爾數 561:首先 2560 mod 561=1,通過了費馬測試;進而考慮到 2560=(2280)2,根據規律 [1]

if,then, a mod p=k a2 mod p=k2 mod p

結合上面的定理,那么 2280 mod 561 也應該是 1560,經驗證 2280 mod 561=1;繼續我們發現 2140 mod 561=67,于是馬上可以得到 672 mod 561=1,違反了上面的定理,由此可以判斷 561 不是質數。

在Miller-Rabin算法中,我們將 p?1 (一定是偶數)不斷提取因子2,最終分解為:2sd(其中 d 是奇數),對于所有的 r[0,s?1],只要存在 r 使得 ad mod p=1a2rd mod p=p?1,即可推斷 p 有可能為素數;反之則一定為合數。

有趣的是在上面Scheme實現的費馬測試算法中,計算 an?1 所用的正是類似將指數不斷除2的遞歸方法,因此我們只需要對(expmod base exp n)方法中的遞歸調用(remainder (square (expmod base (/ exp 2) m)) m)(恰好是規律 [1])進行檢驗:

;; miller-rabin algorithm
(define (remainder-square a n)
  (cond ((or (= a 1) (= a (- n 1))) 1)
        ((= (remainder (square a) n) 1) 0)
        (else (remainder (square a) n))))

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp) (remainder-square (expmod base (/ exp 2) m) m))
        (else (remainder (* base (expmod base (- exp 1) m)) m))))

(define (mr-test n a)
  (= (expmod a (- n 1) n) 1))

(define (miller-rabin n t)
  (cond ((= t 0) #t)
        ((mr-test n (+ 2 (random (- n 2)))) (miller-rabin n (- t 1)))
        (else #f)))

(miller-rabin 561 20)
;;=> #f

2. RSA 加密算法

RSA 算法被評為真正統治世界的十大算法之一,雖然并非經過嚴格的權威性論證與投票表決,但大部分人應該不會對此存在異議。RSA 算法的數學原理簡而言之就是:利用對大數進行因數分解的困難,實現以公開密鑰對明文進行加密,而只能以私有密鑰進行解密。具體的數學推導過程可以參考文章最后的鏈接深入了解,這里我們需要了解一個新的數學定理:歐拉函數

對整數 n,歐拉函數 φ(n) 是小于或等于 n 的正整數中與 n 互質的數的數量。特別的,如果 n 是素數,φ(n)=n?1

下面我們來看Scheme簡單實現的 RSA 算法及加密解密過程的實例:

;; 1. 隨意選擇兩個大的質數p和q,p不等于q
(define p 61)
(define q 53)
;; 計算N=pq
(define N (* p q))

;; 2. 根據歐拉函數,求得r=(p-1)(q-1)
(define r (* (- p 1) (- q 1)))

;; 3. 選擇一個小于r的整數e,e與r互質
(define e 17)
;; 求得e關于r的模反元素
(define d (modular-multiplicative-inverse e r))

;; 模反元素計算過程
(define (dividable? x y)
  (= (remainder x y) 0))
(define (mmii a m k)
  (if (dividable? (+ (* k m) 1) a)
    (/ (+ (* k m) 1) a)
    (mmii a m (+ k 1))))
(define (modular-multiplicative-inverse a m)
  (mmii a m 1))

;; RSA 加密需要公鑰(N, e)
;; RSA 解密需要私鑰(N, d)
(define (rsa m KNN Ked)
  (expmod m Ked KNN))

;; 4. 利用公鑰對明文m進行加密
(define m 1990)
(display "PlaintText: ")
(display m)
(newline)
(display "Encrypted: ")
(define cipher (rsa m N e))
(display cipher)
(newline)

;;=> PlaintText: 1990
;;=> Encrypted: 1806

;; 5. 根據私鑰對密文進行解密
(display "Decrypted: ")
(display (rsa cipher N d))

;;=> Decrypted: 1990

想要根據公鑰 (N,e) 推算出私鑰 (N,d) ,需要對 N 進行因數分解,推導出原始的 pq。上例中用到的 p=61,q=53 很容易就可以從 N=3233 推導出來,然而當 N 非常大(1024或2048位二進制)時,對其進行因數分解在當下看來幾乎是不可能實現的。這也是密碼學中的一個基本原理,如果解密所需的成本高于該信息的價值,那么這種加密就被認為是安全的。

參考

原文網址:http://blog.rainy.im/2015/08/27/miller-rabin-n-rsa/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!