出于无聊, 打算从头实现一遍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 times", i)); return init; } }