从零撸一个RSA (JAVA)

 

从零撸一个RSA加密算法

对于RSA算法,一个非对称加密算法,具体是用公钥加密,私钥解密。主要原理是模逆,详细的背后的数学原理参看文末链接。

对于生成密钥流程:

 

对于加解密过程:

 

如此如下:

辅助容器类 MyKey.java

import java.math.BigInteger;

public class MyKey {

	
	public BigInteger key;
	public BigInteger base;
	
	public MyKey(BigInteger key , BigInteger base ) {
		this.key = key;
		this.base = base;
	}
	public MyKey() {
		
	}
	@Override
	public String toString() {
		return base.toString() + "\t" + key.toString() ;
	}

}

 

 

工具类 Util.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.math.BigInteger;
import java.util.Random;

//工具类

public class Util {

     // 求公约数
	public BigInteger gcd( BigInteger a, BigInteger b ) {
	   if( b.equals(BigInteger.ZERO) ) {
		   return a;
	   }
	   return gcd(b, a.mod(b));
	}
	
	
    // 快速幂取模
	public BigInteger powMod(BigInteger base, BigInteger pow , BigInteger m) {
		BigInteger res = BigInteger.ONE;
		
	    while(true) {
	    	if (pow.equals(BigInteger.ZERO)) {
				break;
			}
	    	if(pow.mod(new BigInteger("2")).equals(BigInteger.ONE)) {
	    		res = res.multiply(base).mod(m);
	    	}
	    	base = base.multiply(base).mod(m);
	    	pow = pow.divide(new BigInteger("2"));
	    }
		
		return res;
	}
	
    // 扩展里欧几里得算法求ax + by = gcd(a,b),返回值是公约数 g , x , y
	public BigInteger[] exGcd(BigInteger a, BigInteger b ) {
		
		BigInteger[] res = new BigInteger[3];
		if( b.equals(BigInteger.ZERO) ) {
			res[0] = b; 
			res[1] = BigInteger.ONE;
			res[2] = BigInteger.ZERO; 
		}else {
			BigInteger[] tmp = exGcd(b, a.mod(b));
			res[0] = tmp[0];
			res[1] = tmp[2];
			res[2] = tmp[1].subtract( a.divide(b).multiply(tmp[2]) );
		}
		return res;
	}
	
	
    //获得与val互质的数
	public BigInteger getRelativelyPrime( BigInteger val ) {
		BigInteger res;
		while(true) {
			res = BigInteger.probablePrime(200, new Random());
			if(gcd(res, val).equals(BigInteger.ONE)) {
				return res;
			}
		}
	}
    
    // 读取存在prime.txt两个质数p ,q 
	public MyKey getPQ() {
		MyKey myKey = new MyKey();
		try {
			FileReader fileReader = new FileReader(new File("./prime.txt"));
			BufferedReader bufferedReader = new BufferedReader(fileReader);
			String p = bufferedReader.readLine() ; 
			String q = bufferedReader.readLine() ;
			myKey.base = new BigInteger(p);
			myKey.key = new BigInteger(q);
			bufferedReader.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return myKey;
	}
	
	
	public static void main(String[] args) {
		Util util = new Util();
	    BigInteger aBigInteger = new BigInteger("2");
	    BigInteger bigInteger = new BigInteger("5");
	    BigInteger cBigInteger = new BigInteger("20");
	    
	    
	    System.out.println(util.getPQ());
	    
	    BigInteger[] res = util.exGcd(aBigInteger, bigInteger);
	    System.out.println("" + res[0].toString()  + "\t"+ res[1].toString() + "\t" +res[2].toString());
	    
	    System.out.println(util.powMod(aBigInteger, bigInteger, cBigInteger));
	    
	    
	}

}




 

主要类RSA.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;

public class RSA {
	
	
	private Util util ;
	private MyKey pq;
	BigInteger n;
	BigInteger phi;
	

    // 初始化 p ,q , n = p*q , phi = (p-1)*(q-1)
	public RSA() {
		util = new Util();
		pq = util.getPQ();
		n = pq.base.multiply(pq.key);
		phi = pq.base.subtract(BigInteger.ONE).multiply(pq.key.subtract(BigInteger.ONE));
	}
	
	
    // 随机产生一个公钥 gcd(e,phi) == 1 
	public MyKey getPublicKey() {
		MyKey publicKey = new MyKey();
		BigInteger e = util.getRelativelyPrime(phi);
		publicKey.base = n;
		publicKey.key = e;
		return publicKey ; 
	}
	
    // 产生私钥d , d*e mod phi == 1  ---> d*e - k*phi = 1  
	public MyKey getPersonKey(MyKey publicKey) {
		MyKey personKey = new MyKey();
		BigInteger[] res = util.exGcd(publicKey.key, phi);

        // 如果求得私钥d是负数则需要化为正数 d = k * phi/gcd(e,phi) + d
		while( res[1].compareTo(BigInteger.ZERO) == -1 ) {
			res[1] = res[1].add(phi);
		}
		personKey.base = n;
		personKey.key = res[1];
		return personKey;
	}
	
    // 将数值编码
	public BigInteger encode(BigInteger val, MyKey publicKey) {
		BigInteger res = util.powMod(val, publicKey.key, publicKey.base); 
		return res;
	}
	
    //  将数值解码
	public BigInteger decode(BigInteger enVal, MyKey personKey) {
		BigInteger res = util.powMod(enVal, personKey.key, personKey.base);
		return res;
	}
	
	// 对字符串编码,存入文件
	public String encode(String val, MyKey publicKey, String pathname) {
		
		StringBuilder sb = new StringBuilder();
		
        File sFile = new File(pathname);
		if (sFile.exists() ) {
			sFile.delete();
		}
		
		for( int i = 0 ; i < val.length() ; i++ ) {
             
             // 强转为数值型编码
			 BigInteger m = BigInteger.valueOf((long)val.charAt(i));
			 BigInteger c = encode(m,publicKey);
			 
			try {
				FileWriter fileWriter = new FileWriter(pathname, true);
				fileWriter.write(c.toString() + "\n");
				fileWriter.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		return sb.toString();
	}
	
    
    // 解码
	public String decode(MyKey personKey, String pathname) {
		StringBuilder sb = new StringBuilder();
		FileReader fileReader;
		try {
			fileReader = new FileReader(new File(pathname));
			BufferedReader bufferedReader = new BufferedReader(fileReader);
			String buf;
			while(  (buf = bufferedReader.readLine()) != null ) {
				sb.append( (char)Long.parseLong(decode(new BigInteger(buf), personKey).toString()) );
			}
			bufferedReader.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return sb.toString();
	}
}

 

测试类 Main.java

import java.math.BigInteger;

public class Main {
	
public static void main(String[] args) {
		
		String string = "嗨呀,好气啊,你破又破不了";
		
		RSA rsa = new RSA();
        
        //公钥
		MyKey pKey = rsa.getPublicKey();
		System.out.println(pKey);
		
       //私钥
		MyKey prKey = rsa.getPersonKey(pKey);
		System.out.println(prKey);
		
        // 判断 e*d mod phi == 1
		System.out.println(prKey.key.multiply(pKey.key).mod(rsa.phi));
		
        //测试数值
		BigInteger ens = rsa.encode(new BigInteger("123456789"), pKey);
		System.out.println(ens);
		
		BigInteger des = rsa.decode(ens, prKey);
		System.out.println(des);
		
        //测试字符串
		rsa.encode(string, pKey, "./encode.txt");
		
		string = rsa.decode(prKey, "./encode.txt");
		
		System.out.println(string);
		
	}
}

 

prime.txt 中两个100位的素数

5202642720986189087034837832337828472969800910926501361967872059486045713145450116712488685004691423
7212610147295474909544523785043492409969382148186765460082500085393519556525921455588705423020751421

对于C++版参看 https://www.cnblogs.com/nanashi/p/6701030.html

对于素数的来源可以参看 https://primes.utm.edu/lists/small/

对于RSA的详细数论原理参看 https://blog.csdn.net/qwe6112071/article/details/53576584

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值