出於無聊, 打算從頭實現一遍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; } }