重复造轮子之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
性能不到标准库的十分之一
附上代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 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; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用