• 重复造轮子之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	times", i));
            return init;
        }
    }
    

      

  • 相关阅读:
    04 body中的相关标签
    03 我的第一个html页面
    02 标签的分类
    01 前端初识
    10-pymysql的应用
    NOIP2012提高组初赛总结(题目+易错点+解析)
    NOIP2011提高组初赛总结(题目+易错点+解析)
    浅谈哈夫曼编码
    关于P类问题,NP问题,NPC问题的一些粗浅理解
    NOIP2010提高组初赛总结(题目+易错点+解析)
  • 原文地址:https://www.cnblogs.com/stevenczp/p/4299072.html
Copyright © 2020-2023  润新知