GitHub RSA密碼 RSA密碼是1978年美國麻省理工學院三位密碼學者R.L.Rivest、A.Shamir和L.Adleman提出的一種基於大合數因數分解困難性的公開密鑰密碼。由於RSA密碼既可用於加密,又可用於數字簽名,通俗易懂,因此RSA密碼已成為目前應用最廣泛的公開密鑰密碼。 RSA加 ...
RSA密碼
RSA密碼是1978年美國麻省理工學院三位密碼學者R.L.Rivest、A.Shamir和L.Adleman提出的一種基於大合數因數分解困難性的公開密鑰密碼。由於RSA密碼既可用於加密,又可用於數字簽名,通俗易懂,因此RSA密碼已成為目前應用最廣泛的公開密鑰密碼。
RSA加解密演算法
1.隨機地選擇兩個大素數p和q,而且保密;
2.計算n=pq,將n公開;
3.計算φ(n)=(p-1)(q-1),對φ(n)保密;
4.隨機地選取一個正整數e,1<e<φ(n)且(e,φ(n))=1,將e公開;
5.根據ed=1(mod φ(n)),求出d,並對d保密;
6.加密運算:C=Me(mod n);
7.解密運算:M=Cd(mod n)。
註意:在加密運算和解密運算中,M和C的值都必須小於n,也就是說,如果明文(或密文)太大,必須進行分組加密(或解密)。
RSA演算法細節
實現RSA演算法,主要需要實現以下幾個部分:
1.對大數的素數判定;
2.模逆運算;
3.模指運算。
對大數的素數判定
一個較小的數是否為素數,可以用試除法來判定,而如果這個數很大的話,試除法的效率就會變得很低下。也就是說,試除法不適用於對大數進行素數判定,所以對大數的素數判定一般採用素數的概率性檢驗演算法,其中又以Miller演算法最為常見。
使用素數的概率性檢驗演算法判定一個數是否為素數,雖然相比試除法而言效率非常之高,但是對該數的判定結果並不准確。該演算法通過迴圈使用Miller演算法來提高判定結果的正確性。
素數的概率性檢驗演算法的流程:對於奇整數n,在2~n-2之間隨機地選取k個互不相同的整數,迴圈使用Miller演算法來檢驗n是否為素數。若結果為true,則認為n可能為素數,否則肯定n為合數。
一輪Miller演算法判定大整數n不是素數的概率≤4-1,所以,素數的概率性檢驗演算法判定大整數n不是素數的概率≤4-k(k為Miller演算法的迴圈次數)。
Miller演算法
若n為奇素數,則對∀a∈[2,n-2],由於a與n互素,根據歐拉定理可得aφ(n)=an-1=1(mod n)。
若n是奇素數,則不存在1(mod n)的非平凡平方根,即對於x2=1(mod n)的解有且僅有±1。
若n是奇素數,則n-1是偶數。不妨令n=2tm+1(t≥1),則m為n-1的最大奇因數。根據上述兩點,不難得出,對∀a∈[2,n-2],∃τ∈[1,t]使得
Miller演算法正是通過上述的逆否命題而設計出來的,其原理是:對∀a∈[2,n-2],n是一個合數的充要條件是對∀τ∈[1,t]使得
Miller演算法的設計思路:令b=am(mod n),如果b=±1(mod n)則n可能是一個素數;否則,b=b2(mod n),並判斷是否滿足b=-1(mod n)(滿足則n可能是一個素數),由此迴圈t-1次。如果都滿足b≠-1(mod n),則n一定是一個合數。
e.m.判定221是否為素數
n=221=22*55+1
所以m=55,t=2
取a=174,則
17455(mod 221)=47
174110(mod 221)=220
所以n要麼是一個素數,要麼a=174是一個“強偽證”。
再取a=137,則
13755(mod 221)=188
137110(mod 221)=205
所以n是一個合數。
模逆運算
模逆運算就是求滿足方程ax=1(mod m)的解x,而ab=1(mod m)有解的前提條件是(a,m)=1,即a和m互素。
對方程ax=1(mod m)的求解可以轉換為求解ax+my=1=(a,m),即轉換為擴展歐幾里德演算法。
e.m.求243-1(mod 325)
325=1*325+0*243
243=0*325+1*243
82=325-243=1*325+(-1)*243
79=243-2*82=(-2)*325+3*243
3=82-79=3*325+(-4)*243
1=79-26*3=(-80)*325+107*243
所以
243-1(mod 325)=107
模指運算
模指運算就是對an(mod m)的計算。當指數n的值較大時,如果先求出bn再去模m的話,效率會很低下。所以,對於指數n較大的情況一般採用反覆平方乘演算法。
反覆平方乘演算法
所以,反覆平方乘演算法的原理是將指數n轉化為2的冪之和的形式,即n=2kek+2k-1ek-1+...+2e1+e0,然後根據l1=a2(mod m),l2=a4(mod m)=l12(mod m),...,
最後根據an(mod m)=e0a·e1l1·...·eklk(mod m)求解。
e.m.求2335(mod 101)
35=32+2+1
231(mod 101)=23
232(mod 101)=24
234(mod 101)=242(mod 101)=71
238(mod 101)=712(mod 101)=92
2316(mod 101)=922(mod 101)=81
2332(mod 101)=812(mod 101)=97
所以
2335(mod 101)=97×24×23(mod 101)=14
RSA密碼的安全性
密碼分析者攻擊RSA密碼的一種可能途徑是截獲密文C,通過M=Cd(mod n)求出M。其中,n是公開的,而d是保密的。因為e是公開的,所以可以通過ed=1(mod φ(n))求出d,而φ(n)是保密的。雖然n是公開的,但是φ(n)=(p-1)(q-1)=pq-p-q+1=n-p-q+1,其中p和q是保密的,也就是說欲求得φ(n)必須知道p和q,即必須將n進行因式分解。
小合數的因數分解是容易的,然而大合數的因數分解卻是十分困難的。由此可以得出,破譯RSA密碼的困難性約為對n進行因數分解的困難性。
RSA密碼的安全性除了與因數分解密切相關之外,還與其參數p、q、e、d的選取有密切關係。只要合理地選取參數,並正確使用,RSA密碼就是安全的。這就是目前RSA密碼仍然廣泛使用的重要原因。
Java實現
RSA
1 p = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random()); 2 q = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random()); 3 n = p.multiply(q); 4 phi_n = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE)); 5 do { 6 e = new BigInteger(new Random().nextInt(phi_n.bitLength() - 1) + 1, new Random()); 7 } while (e.compareTo(phi_n) != -1 || e.gcd(phi_n).intValue() != 1); 8 d = e.modPow(new BigInteger("-1"), phi_n);
1 /** 2 * 加密 3 * <p> C=M^e(mod n) 4 * @param M 5 * @param n 6 * @param e 7 * @return 8 */ 9 public static BigInteger encrypt(BigInteger M, BigInteger n, BigInteger e) { 10 return M.modPow(e, n); 11 }
1 /** 2 * 解密 3 * <p> M=C^d(mod n) 4 * @param C 5 * @param n 6 * @param d 7 * @return 8 */ 9 public static BigInteger decrypt(BigInteger C, BigInteger n, BigInteger d) { 10 return C.modPow(d, n); 11 }
對大數的素數判定
1 /** 2 * 素數的概率性檢驗演算法 3 * @param k 4 * @param n 5 * @return 6 */ 7 static boolean isPrime(int k, long n) { 8 List<Long> a = new ArrayList<Long>(); 9 int t = n - 2 > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) (n - 2); 10 do { 11 long l = (long) (new Random().nextInt(t - 2) + 2); 12 if (-1 == a.indexOf(l)) 13 a.add(l); 14 } while (a.size() < k); 15 for (int i = 0; i < k; i++) 16 if (! Miller(n, a.get(i))) 17 return false; 18 return true; 19 } 20 static boolean Miller(long n, long a) { 21 long m = n - 1; 22 int t = 0; 23 while (m % 2 == 0) { 24 m /= 2; 25 t++; 26 } 27 long b = modExp(a, m, n); 28 if (b == 1 || b == n - 1) 29 return true; 30 for (int j = 1; j < t; j++) { 31 b = b * b % n; 32 if (b == n - 1) 33 return true; 34 } 35 return false; 36 }
模逆運算
1 /** 2 * 模逆運算 3 * @param b 4 * @param m 5 * @return b^-1(mod m) 6 */ 7 static long modInv(long b, long m) { 8 if (b >= m) b %= m; 9 return exGcd(b, m)[1] < 0 ? exGcd(b, m)[1] + m : exGcd(b, m)[1]; 10 } 11 /** 12 * 擴展歐幾里德演算法 13 * <p>(a,b)=ax+by 14 * @param a 15 * @param b 16 * @return 返回一個long數組result,result[0]=x,result[1]=y,result[2]=(a,b) 17 */ 18 static long[] exGcd(long a, long b) { 19 if (a < b) { 20 long temp = a; 21 a = b; 22 b = temp; 23 } 24 long[] result = new long[3]; 25 if (b == 0) { 26 result[0] = 1; 27 result[1] = 0; 28 result[2] = a; 29 return result; 30 } 31 long[] temp = exGcd(b, a % b); 32 result[0] = temp[1]; 33 result[1] = temp[0] - a / b * temp[1]; 34 result[2] = temp[2]; 35 return result; 36 }
模指運算
1 /** 2 * 模指運算 3 * @param b 4 * @param n 5 * @param m 6 * @return b^n(mod m) 7 */ 8 static long modExp(long b, long n, long m) { 9 long result = 1; 10 b = b % m; 11 do { 12 if ((n & 1) == 1) 13 result=result*b%m; 14 b = b * b % m; 15 n = n >> 1; 16 } while (n != 0); 17 return result; 18 }
測試
測試數據
p=e86c7f16fd24818ffc502409d33a83c2a2a07fdfe971eb52de97a3de092980279ea29e32f378f5e6b7ab1049bb9e8c5eae84dbf2847eb94ff14c1e84cf568415
q=d7d9d94071fcc67ede82084bbedeae1aaf765917b6877f3193bbaeb5f9f36007127c9aa98d436a80b3cce3fcd56d57c4103fb18f1819d5c238a49b0985fe7b49
e=10001
M=b503be7137293906649e0ae436e29819ea2d06abf31e10091a7383349de84c5b
測試結果
參考文獻
張煥國,唐明.密碼學引論(第三版).武漢大學出版社,2015年