Rabin简单加解密

算法描述

程序分为两个部分,一部分加密,一部分解密。
解密部分:先通过生成大素数算法生成公钥n和私钥p、q,然后运用广义欧几里得除法计算s,t使sp+tq=1,然后输入用公钥加密的密文c,然后计算同余式x^2=c(modn)的四个根(在求解时可以不用中国剩余定理,直接采用当p=q=3(mod4)时的定理,直接由s,t求解),然后用校验值判断出真正的解,得到明文。
加密部分:先将文本转换为整数,然后在转换后的值中加入校验值,然后利用获得的公钥计算c=m^2(modn)得到密文。

加密类中

1、 生成大素数直接采用java中自带的大数的类的函数BigInteger.probablePrime(n,rnd),rnd为随机数因子,n为生成的随机大素数的二进制的位数,设置n为100时的生成结果如下:
Rabin简单加解密
2、 运用广义欧几里得除法时,定义两个数组long s[]= {1,0,0};long t[]= {0,1,0}s[0]为s-2,s[1]为s-1,t同理;设置while循环,当r2!=0时进行循环运算,最后s,t的大小即为s[1]和t[1]。相关代码如下:
Rabin简单加解密
3、 解密过程中对于二次同余方程,当p=q=3(mod4)用如下解的公式,u=c^(p+1)/4。v=c^(q+1)/4。x=(tqu+spv)(modn)。y=(tqu-spv)(modn)。四个解分别为x,y,n-x,n-y,将相关公式用程序式表示,程序如下:
Rabin简单加解密
4、 进行校验部分,因为会解出四个值,所以在进行加密时加入了校验值,解密时需要根据校验值找出四个解中真正的解,程序中采用在密文的二进制后加入额外的01字符串作为校验值,所以在解出四个解后,将其转化为二进制字符串,将其末尾的01字符串与校验值进行比较,最后将校验值删除,再将二进制字符串转换得到明文,此处校验值为010,程序如下:
Rabin简单加解密

解密类中

1、 将文本形式的明文进行编码,转换为整数,这里先将输入的字符串转换为char型数组,再转换为二进制数,再将二进制数转换为十进制整数,程序如下:
Rabin简单加解密
2、 添加校验值,为了能从四个解中找出唯一的解,需要在值后面加入校验值,这里先将明文转换为二进制字符串,再在字符串的末尾加入01的二进制校验值,程序中校验值为010,程序如下:
Rabin简单加解密
3、 最后由得到的公钥计算c=m^2(modn)得到密文c,程序如下:
Rabin简单加解密
另:由于所需计算的数字远远超出了java的int,long的变量类型,所以在程序的运算中运用了java的BigInteger类,可以用该类的方法计算大数的加减乘除等运算,相关方法如下:
BigInteger.add(b)); //大整数加法,b也为一个大数
BigInteger.subtract(b)); //大整数减法
BigInteger.multiply(b)); //大整数乘法
BigInteger.divide(b)); //大整数除法(取整)
BigInteger.remainder(b)); //大整数取模
BigInteger.pow(exponent)); //大整数a的exponent次幂

程序

加密

package rabin;

import java.math.BigInteger;
import java.util.Scanner;

public class Encrypt {

    public static void main(String[] args) {
        
        System.out.println("请输入 ");
        Scanner in = new Scanner(System.in);
        String str = in.next();
        
        
        //将string转化为char数组,再转化为2进制字符串,再转换为整数
        char[] strchar = str.toCharArray();
        long binary=0;
        for(int i=0;i<strchar.length;i++) {
            binary=binary+Integer.parseInt((Integer.toBinaryString(strchar[i])),2);
        }
        System.out.println("明文为: "+binary);
        
        //添加校验信息,这里设置为010
        String strbinary=Long.toString(Integer.parseInt(Long.toBinaryString(binary)+"010",2));
        binary=Long.valueOf(strbinary);
        
        System.out.println("请输入公钥:  ");
        BigInteger n = in.nextBigInteger();
        BigInteger m = new BigInteger(Long.toString(binary*binary));
        BigInteger c = m.remainder(n);
        System.out.println("加密结果为:"+c);
        
    }

}

解密

package rabin;

import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

public class Decrypt {

    public static void main(String[] args) {
        //生成大素数
        Random rnd = new Random();
        BigInteger p = BigInteger.probablePrime(20,rnd);//生成2进制为n位的大素数p,n可以设置,为了方便这里设置为20
        BigInteger q = BigInteger.probablePrime(20,rnd);//生成2进制为n位的大素数q
        BigInteger n = p.multiply(q);//私钥p,q相乘得到公钥n
        
        
        System.out.println("公钥为: "+n+" 私钥为: "+p+"和"+q);
        System.out.println("输入要解密的内容");
        Scanner in = new Scanner(System.in);
        BigInteger c = in.nextBigInteger();
        
        
        //用广义欧几里得出发求s,t使s*p+t*q=1;
        long s[]= {1,0,0};
        long t[]= {0,1,0};
        long r1=p.longValue();
        long r2=q.longValue();
        long tmp;
        long i=r1/r2;
        tmp=r2;
        r2=r1%r2;
        r1=tmp;
        while(r2!=0) {
            s[2]=s[0]-i*s[1];
            t[2]=t[0]-i*t[1];
            s[0]=s[1];
            s[1]=s[2];
            t[0]=t[1];
            t[1]=t[2];
            if(r2!=0)
                i=r1/r2;
            tmp=r2;
            r2=r1%r2;
            r1=tmp;
        }
        BigInteger S=BigInteger.valueOf(s[1]);
        BigInteger T=BigInteger.valueOf(t[1]);
        
        //开始解密
        int P=(p.intValueExact()+1)/4;
        int Q=(q.intValueExact()+1)/4;
        BigInteger u=(c.pow(P)).remainder(p);
        BigInteger v=(c.pow(Q)).remainder(q);
        BigInteger x1 = ((T.multiply(q.multiply(u))).add(S.multiply(p.multiply(v)))).remainder(n);
        BigInteger x2 = n.subtract(x1);
        BigInteger y1 = ((T.multiply(q.multiply(u))).subtract(S.multiply(p.multiply(v)))).remainder(n);
        BigInteger y2 = n.subtract(y1);
        
        //用校验值对四个解密结果进行判断
        String a= x1.toString(2);
        String b= x2.toString(2);
        String e= y1.toString(2);
        String d= y2.toString(2);
        String result[]= {a,b,e,d};
        for(int j=0;j<result.length;j++) {
            if(result[j].lastIndexOf("010",result[j].length()-3)==result[j].length()-3) {
                result[j]=result[j].substring(0,result[j].length()-3);
                System.out.println("解密得: "+Long.valueOf(result[j], 2));
                break;
            }
        }
        
    }
    
}

运行结果

加密
Rabin简单加解密
解密
Rabin简单加解密

上一篇:文件读入最大子数组


下一篇:c#-将所有低序位设置为0,直到剩下两个1(用于存储为字节数组的数字)