import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
@SuppressWarnings("all")
public class RSA5 {
private static final Integer READER_1M = (2<<9) * 1;
private static final Integer UNIT_SIZE = 2<<8;
private static final String DEFAULT_TIME_SHOW = "yyyy-MM-dd hh:mm:ss";
private static final String DEFAULT_TIME_LINE = "yyyyMMdd_hhmm_ss_SSS";
private static final String RSA = "RSA";
private static final String BC = "BC";
private static final String UTF8 = "UTF-8";
private static final String PUBLIC_KEY_FILE = getClassPath(RSA5.class) + "request.key";
private static final String PRIVATE_KEY_FILE = getClassPath(RSA5.class) + "response.key";
private String prky;
private String puky;
private static Size S1024;
private static Size S2048;
private static Size S3072;
private static Size S7680;
private static Size S15360;
private static Size thisSize;
static {
S1024 = Size.create(2 * (2 << 8));
S2048 = Size.create(4 * (2 << 8));
S3072 = Size.create(6 * (2 << 8));
S7680 = Size.create(15 * (2 << 8));
S15360 = Size.create(30 * (2 << 8));
thisSize = S1024 ;
}
public RSA5() {
this.readKeyPair();
}
public static final String getClassPath(Class clazz) {
return clazz.getClassLoader()
.getResource("")
.getPath()
.substring(1);
}
private static String getNowDate() {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_TIME_SHOW);
return format.format(date);
}
private static String getLineDate() {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_TIME_LINE);
return format.format(date);
}
public static KeyPair generateKeyPair() {
try {
KeyPairGenerator kpg;
Security.addProvider(new BouncyCastleProvider());
kpg = KeyPairGenerator.getInstance(RSA, BC);
assert kpg!=null : "key generate error.";
kpg.initialize(thisSize.getKeySize());
return kpg.generateKeyPair();
} catch (Exception e) {
return null;
}
}
private static boolean areKeysPresent() {
File privateKey = new File(PRIVATE_KEY_FILE);
File publicKey = new File(PUBLIC_KEY_FILE);
return privateKey.exists() && publicKey.exists();
}
private static void saveKeyPair(KeyPair keyPair) {
if(areKeysPresent()) return;
writeDataBuffer(new File(PUBLIC_KEY_FILE), loadPublicKey(keyPair.getPublic()));
writeDataBuffer(new File(PRIVATE_KEY_FILE), loadPrivateKey(keyPair.getPrivate()));
}
private void readKeyPair() {
this.prky = readDataOnce(PRIVATE_KEY_FILE);
this.puky = readDataOnce(PUBLIC_KEY_FILE);
}
private static PrivateKey loadPrivateKey(String key) {
try {
assert key!=null : "private-key is null.";
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
java.util.Base64.getDecoder().decode((key.getBytes()))
);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
return keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
error(e.getMessage());
return null;
}
}
private static PublicKey loadPublicKey(String key) {
try {
if (key == null) {
throw new RuntimeException("public-key is null.");
}
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
java.util.Base64.getDecoder().decode((key.getBytes()))
);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
error(e.getMessage());
return null;
}
}
private static String keyToString(Key key) {
try {
byte[] keyBytes = key.getEncoded();
return new String(java.util.Base64.getEncoder().encode(keyBytes), UTF8);
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
private static String loadPrivateKey(PrivateKey privateKey) {
return keyToString(privateKey);
}
private static String loadPublicKey(PublicKey publicKey) {
return keyToString(publicKey);
}
private static void writeData(
File file,
byte[] data
) {
try {
FileOutputStream out = new FileOutputStream(file);
assert data != null : "write data is null.";
out.write(data);
out.flush();
out.close();
} catch (Exception e) {
error(e.getMessage());
}
}
private static void writeDataBuffer(
File file,
String content
) {
try {
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
file.createNewFile();
FileOutputStream stream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter =
new OutputStreamWriter(stream, UTF8);
BufferedWriter writer = new BufferedWriter(outputStreamWriter);
writer.write(content);
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static String readDataOnce(String filePath) {
try {
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
return new String(bytes, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void encrypt(
String source,
String publicKey,
String filePath
) {
try {
RSAPublicKey rsaPublicKey = (RSAPublicKey) loadPublicKey(publicKey);
assert rsaPublicKey != null : "rsa-publik-key is null";
File file = new File(source);
byte[] sourceData = handleReadData(file);
byte[] data = encryptByPublicKey(sourceData, rsaPublicKey.getEncoded());
assert data != null: "data is null";
file = new File(filePath);
writeData(file, data);
} catch (Exception e) {
error(e.getMessage());
}
}
public void decrypt(
String source,
String privateKey,
String extend
) {
try {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) loadPrivateKey(privateKey);
File file = new File(source);
byte[] sourceDataByte = handleReadData(file);
assert rsaPrivateKey != null : "rsa-private-key is null";
assert sourceDataByte != null : "source-data-byte is null";
byte[] data = decryptByPrivateKey(sourceDataByte, rsaPrivateKey.getEncoded());
file = new File(extend);
writeData(file, data);
} catch (Exception e) {
e.printStackTrace();
}
}
private byte[] handleReadData(
File file
) throws IOException {
try {
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
byte[] tmpBuffer = new byte[READER_1M];
int count;
while ((count = in.read(tmpBuffer)) != -1) {
byteOut.write(tmpBuffer, 0, count);
tmpBuffer = new byte[READER_1M];
}
in.close();
return byteOut.toByteArray();
} catch (Exception e) {
error(e.getMessage());
return null;
}
}
private byte[] handleData(
Cipher cipher,
byte[] fileByte,
int modeIndex
) {
try {
int singleMax = modeIndex == Cipher.DECRYPT_MODE
? thisSize.getDecryptSize() : thisSize.getEncryptSize();
int inputLen = fileByte.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > singleMax) {
cache = cipher.doFinal(fileByte, offSet, singleMax);
} else {
cache = cipher.doFinal(fileByte, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * singleMax;
}
byte[] data = out.toByteArray();
out.close();
return data;
} catch (Exception e) {
error(e.getMessage());
return null;
}
}
private byte[] encryptByPublicKey(
byte[] fileByte,
byte[] publicKeyByte
) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyByte);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return handleData(cipher, fileByte, Cipher.ENCRYPT_MODE);
}
private byte[] decryptByPrivateKey(
byte[] dataStr,
byte[] privateKeyByte
) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyByte);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return handleData(cipher, dataStr, Cipher.DECRYPT_MODE);
}
private static void error(String message) {
System.err.println(" ");
System.err.println(" - error: " + message + " (" + getNowDate() +")");
System.err.println(" ");
}
private static void message(String message) {
System.out.println(" ");
System.out.println(" - info: " + message + " (" + getNowDate() +")");
System.out.println(" ");
}
private static void options(Scanner sc) {
System.out.println("---------------------------");
System.out.println("input options number: ");
System.out.println("1: encrypt file ");
System.out.println("2: decrypt file");
System.out.println("x: exit");
System.out.println("ls: show path");
System.out.println("---------------------------");
String content = sc.nextLine();
switch (content) {
case "1": opt1(sc); break;
case "2": opt2(sc); break;
case "x": sc.close(); break;
case "ls":
System.out.println(getClassPath(RSA5.class));
options(sc);
break;
default:
error("invalid options");
options(sc);
break;
}
}
private static void opt1(Scanner sc) {
System.out.println("---------------------------");
System.out.println("input file name: ");
System.out.println("---------------------------");
String content = sc.nextLine().trim();
if(!"".equals(content)) {
if("=x".equalsIgnoreCase(content)) {
options(sc);
}
if(new File(getClassPath(RSA5.class) + content).exists()) {
RSA5 service = new RSA5();
service.encrypt(getClassPath(RSA5.class) + content, service.puky,getClassPath(RSA5.class) + content + ".sign");
message("encrypt final '" + content + "' to " + content + ".sign ");
} else {
error("not found '" + content + "'. ");
opt1(sc);
}
} else {
opt1(sc);
}
}
private static void opt2(Scanner sc) {
System.out.println("---------------------------");
System.out.println("input file name (.sign): ");
System.out.println("---------------------------");
String content = sc.nextLine().trim();
if(!"".equals(content)) {
if("=x".equalsIgnoreCase(content)) {
options(sc);
}
if(new File(getClassPath(RSA5.class) + content).exists()) {
String lineDate = getLineDate();
RSA5 service = new RSA5();
service.decrypt(getClassPath(RSA5.class) + content, service.prky, getClassPath(RSA5.class) + lineDate + ".json");
message(content + "' to " + lineDate + ".json ");
} else {
error("not found '" + content + "'.");
opt2(sc);
}
} else {
opt2(sc);
}
}
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair();
saveKeyPair(keyPair);
RSA5 service = new RSA5();
service.readKeyPair();
message(" - lv " + thisSize.getKeySize());
Scanner sc = new Scanner(System.in);
options(sc);
}
}
@SuppressWarnings("all")
class Size {
private Integer keySize;
private Integer encryptSize;
private Integer decryptSize;
public static Size create(Integer keySize) {
assert keySize>=512;
assert keySize<=65535;
assert keySize%64==0;
return new Size(keySize)
.setEncryptSize(keySize/8 - 11)
.setDecryptSize(keySize/8);
}
private Size(Integer keySize) {
this.keySize = keySize;
}
public Integer getKeySize() {
return keySize;
}
public Integer getEncryptSize() {
return encryptSize;
}
public Integer getDecryptSize() {
return decryptSize;
}
private Size setKeySize(Integer keySize) {
this.keySize = keySize;
return this;
}
private Size setEncryptSize(Integer encryptSize) {
this.encryptSize = encryptSize;
return this;
}
private Size setDecryptSize(Integer decryptSize) {
this.decryptSize = decryptSize;
return this;
}
}