重復造輪子之RSA算法(一) 大素數生成


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

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM