算法已学习到第一章"数字的算法",这次对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
/**
* 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
/**
* 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有关,这是为什么呢?!继续研究中...