RSA 加密演算法的 java 實現
一、RSA 介紹
以下引自百度百科
RSA 是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。
- 需要注意的是,RSA 加密的安全性並不是依賴於大整數分解,而是求解高次同餘方程的困難性。因為存在一些方法(例如共模攻擊)能夠繞過大整數分解來解密。
二、RSA 演算法流程
- 生成兩個大素數(現較為安全的大小為 1024bit ) \(p\) 和 \(q\),令 \(N=p \cdot q, \; \varphi(N) = (p-1) \cdot (q-1)\)
並選取加密指數 \(e\),使得 \(gcd(e,\varphi(N))=1\),即 \(e\) 和 \(\varphi(N)\) 互素
將 \(e\) 和 \(N\) 公開 - 求解同餘方程 \(e \cdot x = 1 (mod \; \varphi(N))\),解為 \(d\)
若 \(d<0\),則令 \(d=d+\varphi(N)\) - 設明文為 \(c\),加密過程為 \(m=c^e (mod \; N)\),\(d\) 為加密後的密文
- 解密過程為 \(c' = m^d (mod \; N)\),\(c'\) 為解密後的結果,即 \(c'=c\)
三、RSA 演算法的 java 實現
1. 核心內容
1.1 Random 類
- 由於我們需要隨機生成素數,故需要呼叫隨機數,該類將配合後續的 BigInteger 類來生成隨機大素數
1.2 BigInteger 類
- 在 RSA 中,我們需要使用長達 1024bit 的素數,顯然一般的 int 型別並不能滿足我們的要求,所以需要採用 java 中的 BigInteger 類,即高精度整數類
- 在 BigInteger 類中,並不能像 int 類那樣直接使用 + - * / % 進行計算,需要呼叫 BigInteger 類的方法
在此處需要用到的是 add(加法),multiply(乘法) ,divide(除法) 和 remainder(取餘) - 在 BigInteger 類中,也不能直接用 == 判斷兩個元素是否相等,需要呼叫 equals 方法
- 對於常用的值(例如0,1),可以直接使用 BigInteger.ZERO 和 BigInteger.ONE 呼叫
對於其他值,可以通過 BigInteger.valueOf 來生成,例如 BigInteger.valueOf(-1) 生成 -1 對應的高精度整數 - 使用 BigInteger 類中的 probablePrime 方法還能夠生成指定長度的隨機素數,該方法傳入待生成素數的長度 LENGTH 和隨機數種子 rnd 作為引數
1.3 解密指數的求解
- 通過上文的演算法流程可以知道,求解解密指數 \(d\) 就是求解同餘方程 \(e \cdot x = 1 (mod \varphi(N))\)
- 由擴充套件歐幾里得演算法,可以求出整數 \(u,v\),使得 \(u \cdot e + v \cdot \varphi(N) = gcd(e,\varphi(N))=1\)
1.4 擴充套件歐幾里得演算法
-
首先,有定理 \(gcd(a,b)=gcd(b,a \; mod \; b)\)
-
接下來的過程需要一些線性代數中的矩陣相關知識
-
首先,有 \(\left[ \begin{matrix} 1 & 0 \\ 0 & 1 \end{matrix} \right] \cdot \left[ \begin{matrix} a \\ b \end{matrix} \right]= \left[ \begin{matrix} a \\ b \end{matrix} \right]\)
-
令 \(q=[\frac{a}{b}]\),即 \(q\) 為 \(a \div b\) 的整數部分,構建矩陣 \(\left[ \begin{matrix} 0 & 1 \\ 1 & -q \end{matrix} \right]\)
將等式兩端同時乘以上述矩陣,則有 \(\left[ \begin{matrix} 0 & 1 \\ 1 & -q \end{matrix} \right] \cdot \left[ \begin{matrix} a\\b \end{matrix} \right]= \left[ \begin{matrix} b \\ a-q \cdot b \end{matrix} \right]\)
即 \(\left[ \begin{matrix} 0 & 1 \\ 1 & -q \end{matrix} \right] \cdot \left[ \begin{matrix} a\\b \end{matrix} \right]= \left[ \begin{matrix} b \\ a \; mod \; b \end{matrix} \right]\)
-
再令 \(q'=[\frac{b}{a \; mod \; b}]\),重複上述步驟
-
最終有 \(\left[ \begin{matrix} u & v \\ u' & v' \end{matrix} \right] \cdot \left[ \begin{matrix} a\\b \end{matrix} \right]= \left[ \begin{matrix} gcd(a,b) \\ 0 \end{matrix} \right]\)
即求出 \(u \cdot a + v \cdot b = gcd(a,b)\)
-
通過上述方法,可以求解出解密指數 \(d\)
1.5 模冪運算
- 在加密或者解密中,都需要對一個大數進行冪次方後取模的運算
- 使用 BigInteger 類中的 modPow 方法即可
2. java 程式碼
/* RSA.java */
import java.math.BigInteger;
import java.util.Random;
public class RSA {
/* 2x2矩陣相乘 */
private static BigInteger[][] MatrixMultiply(BigInteger[][] matrix_1,BigInteger[][] matrix_2){
BigInteger[][] res = new BigInteger[2][2];//結果矩陣
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
BigInteger temp=BigInteger.ZERO;
/* [i][j]位置的元素=左矩陣第i行 與 右矩陣第j列 依次相乘 */
for(int k=0;k<2;k++){
temp=temp.add(matrix_1[i][k].multiply(matrix_2[k][j]));//求出下標為[i][j]位置的元素值
}
res[i][j]=temp;
}
}
return res;
}
/* 利用擴充套件歐式演算法求出a在模b下的逆 */
static BigInteger InverseElement(BigInteger a,BigInteger b){
/* 初始矩陣 */
BigInteger[][] res = new BigInteger[][]{
{BigInteger.ONE,BigInteger.ZERO},//1 0
{BigInteger.ZERO,BigInteger.ONE} //0 1
};
while(!b.equals(BigInteger.ZERO)){
/* 左乘構建矩陣
* 0 1
* 1 -q
*/
BigInteger q=a.divide(b);
res=MatrixMultiply(new BigInteger[][]{
{BigInteger.ZERO,BigInteger.ONE},
{BigInteger.ONE,q.multiply(BigInteger.valueOf(-1))}
},res);//左乘操作陣
/* 普通的輾轉相除法 */
BigInteger temp=a.remainder(b);
a=b;
b=temp;
}
return res[0][0];
}
public static void main(String[] args){
Random rnd=new Random();//建立隨機數發生器,預設以當前時間為種子
final int LENGTH=1024;//定義p,q位數
final int e_LENGTH=16;//e的位數
BigInteger p,q,N;
BigInteger e,d,pi_N;
BigInteger c,m,res;
p=BigInteger.probablePrime(LENGTH,rnd);//建立1024位的隨機素數p
q=BigInteger.probablePrime(LENGTH,rnd);//建立1024位的隨機素數q
N=p.multiply(q);//N=p*q
System.out.println("[建立p,q]");
System.out.println("p:"+p);
System.out.println("q:"+q);
pi_N=p.subtract(BigInteger.ONE).multiply((q.subtract(BigInteger.ONE)));//pi_N=(p-1)*(q-1)
e=BigInteger.probablePrime(e_LENGTH,rnd);//生成指數e
d=InverseElement(e,pi_N);//獲取e在模pi_N下的逆d
if(d.compareTo(BigInteger.ZERO)<0) d=d.add(pi_N);//保證d為正數
System.out.println("[建立e,d]");
System.out.println("e:"+e);
System.out.println("d:"+d);
c=BigInteger.probablePrime(LENGTH,rnd);//建立明文c
m=c.modPow(e,N);//加密明文c得密文m
System.out.println("[建立明文c]");
System.out.println("c:"+c);
System.out.println("[加密明文c得密文m]");
System.out.println("m:"+m);
res=m.modPow(d,N);//解密密文m得明文res
System.out.println("[解密密文m得明文res]");
System.out.println("res:"+res);
System.out.print("檢驗解密正確性:"+c.equals(res));
}
}