从零撸一个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