出於無聊, 打算從頭實現一遍RSA算法
第一步, 大素數生成
Java的BigInteger里, 有個現成的方法
public static BigInteger probablePrime(int bitLength, Random rnd) {
bitLength是期望生成的素數的二進制位數, rnd是隨機數發生器
函數注釋表明, 這個方法的返回值為合數的概率為2^-100
生成100個1024位的素數, 耗時13471ms
但是顯然我不打算直接使用這個函數, 要做就從最底層做起!
目前的做法是基於費馬素性檢測
假如a是整數,p是質數,且a,p互質(即兩者只有一個公約數1),那么a的(p-1)次方除以p的余數恆等於1。
也就是說, 如果p為素數, 那么對於任何a<p, 有
a ^ p % p == a 成立
而它的逆命題則至少有1/2的概率成立
那么我們就可以通過多次素性檢測, 來減少假素數出現的概率
而素數定理, 又指出了素數的密度與ln(x)成反比, 也就是說, 我們可以先隨機生成一個n bit的整數, 如果不是素數, 則繼續向后取, 那么, 大概取n個數, 就能碰到一個素數
原理大概就是這樣
中間有一些優化, 是為了減少對大整數的直接計算
2015.2.25更新
Miller-Rabin檢測 http://www.matrix67.com/blog/archives/234
Carmichael數: 本身為合數, 但是無論做多少次費馬檢查, 都會被判定為素數
為了避免Carmichael數, 就有了新的檢查方式
1. 如果p是素數,x是小於p的正整數,且x^2 mod p = 1,那么要么x=1,要么x=p-1
2. 盡可能提取因子2,把n-1表示成d*2^r,如果n是一個素數,那么或者a^d mod n=1,或者存在某個i使得a^(d*2^i) mod n=n-1 ( 0<=i<r )
生成100個1024位素數, 耗時182141ms
性能不到標准庫的十分之一
附上代碼如下
package com.steven.rsa;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;
/**
*
* @author steven
*/
public class Utils {
private static Random ran = null;
static {
ran = new SecureRandom();
}
/**
* 計算 base^exp % n
*
* @param base
* @param exp
* @param n
* @return
*/
public static BigInteger expmod(int base, BigInteger exp, BigInteger n) {
if (exp.equals(BigInteger.ZERO)) {
return BigInteger.ONE;
}
if (!exp.testBit(0)) {//如果為偶數
return expmod(base, exp.divide(BigInteger.valueOf(2)), n).pow(2).remainder(n);
} else {
return (expmod(base, exp.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2)), n).pow(2).multiply(BigInteger.valueOf(base))).remainder(n);
}
}
/**
* 費馬測試, 如果返回false, 則n肯定為合數, 如果為true, 則n有一半以上的概率為素數
*
* @param n
* @return
*/
public static boolean fermatTest(BigInteger n) {
int base = 0;
if (n.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) < 0) {
base = ran.nextInt(n.intValue() - 1) + 1;
} else {
base = ran.nextInt(Integer.MAX_VALUE - 1) + 1;
}
if (expmod(base, n, n).equals(BigInteger.valueOf(base))) {
return true;
} else {
return false;
}
}
/**
* Miller-Rabin測試
*
* @param n
* @return
*/
public static boolean passesMillerRabin(BigInteger n) {
int base = 0;
if (n.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) < 0) {
base = ran.nextInt(n.intValue() - 1) + 1;
} else {
base = ran.nextInt(Integer.MAX_VALUE - 1) + 1;
}
BigInteger thisMinusOne = n.subtract(BigInteger.ONE);
BigInteger m = thisMinusOne;
while (!m.testBit(0)) {
m = m.shiftRight(1);
BigInteger z = expmod(base, m, n);
if (z.equals(thisMinusOne)) {
break;
} else if (z.equals(BigInteger.ONE)) {
} else {
return false;
}
}
return true;
}
public static boolean isPrime(BigInteger n) {
//copy自jdk源碼, n的bit數越多, 需要的檢測次數就越少
//注釋說是根據標准 ANSI X9.80, "PRIME NUMBER GENERATION, PRIMALITY TESTING, AND PRIMALITY CERTIFICATES".
//我不知道為什么
int sizeInBits = n.bitLength();
int tryTime = 0;
if (sizeInBits < 100) {
tryTime = 50;
}
if (sizeInBits < 256) {
tryTime = 27;
} else if (sizeInBits < 512) {
tryTime = 15;
} else if (sizeInBits < 768) {
tryTime = 8;
} else if (sizeInBits < 1024) {
tryTime = 4;
} else {
tryTime = 2;
}
return isPrime(n, tryTime);
}
/**
* 多次調用素數測試, 判定輸入的n是否為質數
*
* @param n
* @param tryTime
* @return
*/
public static boolean isPrime(BigInteger n, int tryTime) {
for (int i = 0; i < tryTime; i++) {
if (!passesMillerRabin(n)) {
return false;
}
}
return true;
}
/**
* 產生一個n bit的素數
*
* @param bitCount
* @return
*/
public static BigInteger getPrime(int bitCount) {
//隨機生成一個n bit的大整數
BigInteger init = new BigInteger(bitCount, ran);
//如果n為偶數, 則加一變為奇數
if (!init.testBit(0)) {
init = init.setBit(0);
}
int i = 0;
//基於素數定理, 平均只需要不到n次搜索, 就能找到一個素數
while (!isPrime(init)) {
i++;
init = init.add(BigInteger.valueOf(2));
}
//System.out.println(String.format("try %d\ttimes", i));
return init;
}
}
