算法已学习到第一章"数字的算法",这次对RSA的知识做个总结.
作业要求: 计科1111-1114班第二周讲义、课外作业
/** * 2.1: 判断一个正整数是否为质数的算法。 * 输入:一个长整数a * 输出:返回1(为质数),返回0(非质数) */ public static int isPrime(long a){ if( a<=1 ){ return 0; } if( a==2 ){ return 1; } int sqrt = (int) Math.sqrt(a); //为了调高性能,先判断a是否为偶数或是否能开平方 if(a%2==0 || a%sqrt==0){ return 0; } //跳过所有偶数,只测试a是否能被奇数整除 for(int i=3;i<Math.sqrt(a);){ if(a%i == 0){ return 0; } i += 2; } return 1; }
/** * 2.2: 随机生成一个n bit位的长整数。 * 输入:随机数bit位的长度为n(解释:3bit位,则最大为111,最小为100;n bit位,则该数字二进制长度为n) * 输出:返回该随机数 */ public static long createRndInteger(int n){ Random rnd = new Random(); if(n <= 0){ return 0; } if(n==1){ return rnd.nextInt(2); } if(n>=64){ throw new RuntimeException("long类型的最大值为9223372036854775807(2^64-1),n最大只能为63!"); } //n>=2 时, 二进制数的首位为1 String strBin = "1"; for(int i=1;i<n;i++){ strBin += rnd.nextInt(2); } System.err.println(n + "位二进制数为: " + strBin); //将二进制字符串转换成十进制数 return Long.parseLong(strBin, 2); }
/** * 2.3:随机生成一个n bit位的长质数。 * 输入:随机质数的bit位长度为n * 输出:nbit位长度的质数 * 关键为使用随机化算法判断一个长整数是否为素数(P33)。 */ public static long createRndPrime(int n){ if(n <=1 ){ throw new RuntimeException("n必须大于1..."); } if(n >=64 ){ throw new RuntimeException("n必须小于64..."); } if(n==2){ return createRndInteger(n); } long longPrime; do{ longPrime = createRndInteger(n); //如果随机生成的长整数为偶数,则加1使之为奇数 if(longPrime%2 == 0){ longPrime += 1; } }while(isPrime(longPrime)==0);//生成的长整数不是质数,重新生成长整数 return longPrime; /** * 调用JDK自带的随机生成长质数的方法: * java.math.BigInteger.probablePrime(int bitLength, Random rnd) */ }
/** * 2.4:公开密钥(N,e)的生成算法 * 关键为怎样选择一个与(p-1)(q-1)互质的数e。 */ public static void createPK(int n){ if(n>=32){ throw new RuntimeException("n必须小于32..."); } //随机产生两个n bit且不同的素数p和q, 令N=pq p = createRndPrime(n); do{ q = createRndPrime(n); }while(p==q); N = p * q; //选取与 tmp=(p-1)(q-1) 互素的整数e (若取e为奇数, 且e不能整除tmp,则tmp和e互素) long tmp = (p-1)*(q-1); //从3开始找不能整除tmp的奇数 e = 3; do{ e += 2; }while(tmp%e == 0); System.err.println("公钥产生成功! p="+p+" ,q="+q+" ,(N,e) = "+ "(" + N + "," + e + ")"); }
/** * 2.5:保密密钥(N,d)的生成算法 * 关键是运用扩展Euclid算法,生成e模(p-1)(q-1)的逆元,见书籍25页。 * d为e模(p-1)(q-1)操作的逆, 即 d = (e^-1) % (p-1)(q-1) */ public static void createSK(){ long tmp = (p-1)*(q-1); //根据扩展欧几里得算法求 d = ax+by 的解 long ext[] = extendedEuclid( tmp, e); System.err.println("(x,y,d) = ("+ext[0]+", "+ext[1]+", "+ext[2]+") ,(p-1)(q-1) = "+tmp); // 取y的绝对值 long y = Math.abs(ext[1]); //d = (tmp/y + 1 )*y - tmp; d = tmp-y; System.err.println("私钥产生成功! p="+p+" ,q="+q+" ,(N,d) = "+ "(" + N + "," + d + ")"); } /** * 扩展欧几里得算法 : gcd(a,b) = d = ax+by * @param a * @param b * @return (x,y,d) */ public static long[] extendedEuclid(long a, long b){ // 用ext数组存储(x,y,d) long[] ext = new long[3]; if(b==0){ ext[0] = 1; ext[1] = 0; ext[2] = a; return ext; } ext = extendedEuclid(b, a%b); //递推 long x = ext[1], y = ext[0] - (long) Math.floor(a/b) * x, d = ext[2]; ext[0] = x; ext[1] = y; ext[2] = d; return ext; }
/** * 2.6:RSA加密算法 * 对消息m=25进行加密,生成密文c: c = m^e % N */ public static long encrypt (long m){ // 调用 modexp方法进行加密, 防止 m^e 太大溢出 return (long) modexp(m, e, N); } /** * 模的指数运算: 将 x^y 的计算转换成多项式的计算 * @param x n-bit long x * @param y 指数 * @param N 被模的数 * @return (x ^ y') mod N */ public static double modexp(double x, double y, long N){ if(y == 0){ return 1; } /* * |- [ x^(Math.floor(y/2)) ]^2 , 当y为偶数时 * x^y = | * |- [ x * x^(Math.floor(y/2)) ]^2 , 当y为奇数时 */ double z = modexp( x, Math.floor(y/2), N); if(y%2 == 0){ return Math.pow(z, 2) % N; }else{ return Math.pow(z, 2)*x % N; } }
对密文c进行解密。
/** * 2.7:RSA解密算法 * 对密文c进行解密: m = c^d % N */ public static long decrypt(long c){ // 调用 modexp方法进行解密, 防止 c^d 太大溢出 return (long) modexp(c, d, N); }
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.err.println("请输入原文: "); long m = scanner.nextLong(); System.err.println("******************************************** "); System.err.println("[产生公钥] 请输入产生素数p和q的位数n(n<=31):"); int n = scanner.nextInt(); createPK(n); System.err.println("******************************************** "); System.err.println("[产生私钥] "); createSK(); System.err.println("******************************************** "); //加密 System.err.println(" --------------加密----------------- "); System.err.println("原文: "+m); long c = (long) encrypt(m);//密文 System.err.println("密文: "+c); //解密 System.err.println(" --------------解密----------------- "); long mm = decrypt(c); System.err.println("解密后的原文: "+mm); }
当原文=28,产生素数p和q的位数n=5时,结果正确:
当原文=28,产生素数p和q的位数n=6时,结果错误:
当原文=123,产生素数p和q的位数n=4时,结果正确:
结果正确与否与原文大小和产生素数p和q的位数n有关,这是为什么呢?!继续研究中...