This commit is contained in:
Steven Tracey 2023-05-19 14:42:47 -04:00
parent 72363fe3b7
commit 35177895ce
13 changed files with 472 additions and 52 deletions

1
.gitignore vendored
View File

@ -153,3 +153,4 @@ fabric.properties
.idea/caches/build_file_checksums.ser
certificates/
config.json

View File

@ -10,4 +10,6 @@ repositories {
}
dependencies {
implementation("org.nanohttpd:nanohttpd:2.3.1")
implementation("com.google.code.gson:gson:2.10.1")
}

6
config.json Normal file
View File

@ -0,0 +1,6 @@
{
"certDomain": "localhost",
"sfsPort": 7392,
"sftpPort": 2222,
"httpPort": 8000
}

18
openssl.cnf Normal file
View File

@ -0,0 +1,18 @@
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = State
L = Local
O = Org
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = 127.0.0.1
DNS.1 = localhost

View File

@ -0,0 +1,42 @@
package tech.nevets.sfss;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import java.io.*;
public class Config {
private final static Gson GSON = new Gson();
private JsonObject config;
public Config() {
try {
File configFile = new File("./config.json");
if (!configFile.exists()) {
configFile.createNewFile();
new FileWriter(configFile).write("{}");
}
config = GSON.fromJson(new FileReader(configFile), JsonObject.class);
} catch (IOException e) {
config = new JsonObject();
e.printStackTrace();
}
}
public String getString(String key) {
return config.get(key).getAsString();
}
public int getInt(String key) {
return config.get(key).getAsInt();
}
public short getShort(String key) {
return config.get(key).getAsShort();
}
public boolean getBoolean(String key) {
return config.get(key).getAsBoolean();
}
}

View File

@ -1,7 +1,47 @@
package tech.nevets.sfss;
import tech.nevets.sfss.net.HTTPServer;
import tech.nevets.sfss.net.SFSServer;
import tech.nevets.sfss.net.SFTPServer;
import java.io.IOException;
public class Main {
public static final Config CONFIG = new Config();
public static void main(String[] args) {
StoreManager.generateCertificates();
StoreManager.loadCertificates();
Thread sfsServer = new Thread(() -> {
try {
System.out.println("Starting sfs server...");
new SFSServer(CONFIG.getInt("sfsPort"));
} catch (IOException e) {
e.printStackTrace();
}
});
sfsServer.start();
Thread sftpServer = new Thread(() -> {
try {
System.out.println("Starting sftp server...");
new SFTPServer(CONFIG.getInt("sftpPort"));
} catch (IOException e) {
e.printStackTrace();
}
});
sftpServer.start();
Thread httpServer = new Thread(() -> {
try {
System.out.println("Starting http server...");
new HTTPServer(CONFIG.getInt("httpPort"));
} catch (IOException e) {
e.printStackTrace();
}
});
httpServer.start();
}
}

View File

@ -1,24 +0,0 @@
package tech.nevets.sfss;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Server {
private final SSLServerSocket serverSocket;
public Server(String host, int port) {
InetAddress addr;
try {
addr = InetAddress.getByName(host);
} catch (UnknownHostException e) {
addr = null;
System.out.println("Unknown Host:");
e.printStackTrace();
}
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) factory.createServerSocket(port, 0, addr);
}
}

View File

@ -1,64 +1,85 @@
package tech.nevets.sfss;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
public class StoreManager {
public static final File PUBLIC_KEY = new File("./certificates/public.cer");
public static final File PRIVATE_KEY = new File("./certificates/private.key");
public static final File PRIVATE_PKCS8 = new File("./certificates/private_key_pkcs8.der");
public static final File PUBLIC_CERTIFICATE = new File("./certificates/public.cer");
private static SSLContext sslCtx = null;
public static volatile boolean certificatesLoaded = false;
public static void generateCertificates() {
if (PRIVATE_KEY.exists() && PUBLIC_CERTIFICATE.exists() && PRIVATE_PKCS8.exists()) {
return;
}
String[][] cmds = {
{"openssl", "genrsa", "-out", "./certificates/private.key", "2048"},
{"openssl", "req", "-new", "-x509", "-subj", "/C=US/ST=Nope/L=Haha/O=IncInc/CN=www.example.com", "-key", "./certificates/private.key", "-out", "./certificates/public.cer", "-days", "365"}
{"openssl", "genrsa", "-out", PRIVATE_KEY.getAbsolutePath(), "2048"},
{"openssl", "req", "-new", "-x509", "-config", "./openssl.cnf", "-key", PRIVATE_KEY.getAbsolutePath(), "-out", PUBLIC_CERTIFICATE.getAbsolutePath(), "-days", "365"},
{"openssl", "pkcs8", "-topk8", "-inform", "PEM", "-outform", "DER", "-in", PRIVATE_KEY.getAbsolutePath(), "-out", PRIVATE_PKCS8.getAbsolutePath(), "-nocrypt"}
};
new File("./certificates").mkdirs();
for (String[] cmd : cmds) {
Process ps;
try {
ps = Runtime.getRuntime().exec(cmd);
int exitCode = ps.waitFor(); // Wait for the process to finish
int exitCode = ps.waitFor();
if (exitCode != 0) {
System.err.println("Error: Process exited with code " + exitCode);
return;
}
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
return;
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
return;
}
}
}
public static void loadCertificates() {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
InputStream publicKeyStream = new FileInputStream(PUBLIC_KEY);
X509Certificate publicKey = (X509Certificate) certFactory.generateCertificate(publicKeyStream);
// Load the certificate
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
try (InputStream certStream = new FileInputStream(PUBLIC_CERTIFICATE)) {
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(certStream);
keyStore.setCertificateEntry("certificate", cert);
}
InputStream privateKeyStream = new FileInputStream(PRIVATE_KEY);
X509Certificate privateKey = (X509Certificate) certFactory.generateCertificate(privateKeyStream);
// Load the private key
try (FileInputStream fis = new FileInputStream(PRIVATE_PKCS8)) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] keyBytes = fis.readAllBytes();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(spec);
keyStore.setKeyEntry("privateKey", privateKey, new char[0], new Certificate[]{keyStore.getCertificate("certificate")});
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry("privateKey", privateKey);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, new char[0]);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
certificatesLoaded = true;
} catch (final Exception e) {
e.printStackTrace();
}
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(publicKey, "".toCharArray());
public static SSLContext getSSLContext() {
return certificatesLoaded ? sslCtx : null;
}
}

View File

@ -0,0 +1,35 @@
package tech.nevets.sfss.net;
import fi.iki.elonen.NanoHTTPD;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class HTTPServer extends NanoHTTPD {
public HTTPServer(int port) throws IOException {
super(port);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
public Response serve(IHTTPSession session) {
String uri = session.getUri();
switch (uri) {
case "/public.cer" -> {
Response res;
try {
res = newChunkedResponse(Response.Status.OK, "application/x-x509-ca-cert", new FileInputStream("./certificates/public.cer"));
} catch (FileNotFoundException e) {
res = newFixedLengthResponse("Error");
e.printStackTrace();
}
return res;
}
default -> {
return newFixedLengthResponse(Response.Status.NOT_FOUND, "", "Endpoint not found");
}
}
}
}

View File

@ -0,0 +1,27 @@
package tech.nevets.sfss.net;
public class SFSData {
private final byte length;
public SFSData(byte[] data) {
this.length = data[0];
}
public SFSData(int length) {
if (length > 255 || length < 0) {
throw new IllegalArgumentException("Length outside range 0-255");
}
this.length = (byte) length;
}
public byte getLength() {
return length;
}
public byte[] toBytes() {
return new byte[]{
length
};
}
}

View File

@ -0,0 +1,25 @@
package tech.nevets.sfss.net;
import tech.nevets.sfss.StoreManager;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
public class SFSServer {
private final SSLServerSocket serverSocket;
public SFSServer(int port) throws IOException {
SSLServerSocketFactory factory = StoreManager.getSSLContext().getServerSocketFactory();
serverSocket = (SSLServerSocket) factory.createServerSocket(port);
serverSocket.setEnabledCipherSuites(new String[]{
"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256"
});
serverSocket.setEnabledProtocols(new String[]{"TLSv1.2","TLSv1.3"});
serverSocket.setNeedClientAuth(false);
serverSocket.setWantClientAuth(false);
SSLSocket s = (SSLSocket) serverSocket.accept();
}
}

View File

@ -0,0 +1,10 @@
package tech.nevets.sfss.net;
import java.io.IOException;
public class SFTPServer {
public SFTPServer(int port) throws IOException {
}
}

View File

@ -0,0 +1,217 @@
package tech.nevets.sfss.util;
import java.util.HashMap;
import java.util.Map;
public class UByte extends Number {
private final int VALUE;
private final Map<Integer, Integer> BINARY_VALUE_MAP = new HashMap<>(){{
put(0, 128);
put(1, 64);
put(2, 32);
put(3, 16);
put(4, 8);
put(5, 4);
put(6, 2);
put(7, 1);
}};
private final Map<Integer, Character> DEC_HEX_MAP = new HashMap<>(){{
put(0, '0');
put(1, '1');
put(2, '2');
put(3, '3');
put(4, '4');
put(5, '5');
put(6, '6');
put(7, '7');
put(8, '8');
put(9, '9');
put(10, 'A');
put(11, 'B');
put(12, 'C');
put(13, 'D');
put(14, 'E');
put(15, 'F');
}};
/**
*
* @param value int value from 0-255, or 8 digit binary value. <strong>Make sure the binary values do not have a leading 0</strong>
* @param intBinFlag Set to true if passing an integer value, set to false if passing a binary value
*/
public UByte(int value, boolean intBinFlag) {
if (intBinFlag) { // Integer
if (value <= 255 && value >= 0) {
VALUE = value;
} else {
VALUE = 0;
throw new IllegalArgumentException("Number outside of range 0-255");
}
} else { // Binary
char[] binArray = String.valueOf(value).toCharArray();
if (binArray.length <= 8 && binArray.length > 0) {
int intValue = 0;
int digit = 8 - binArray.length;
for (char c : binArray) {
if (c == '1') {
intValue += BINARY_VALUE_MAP.get(digit);
}
digit++;
}
VALUE = intValue;
} else {
VALUE = 0;
throw new NumberFormatException("Invalid Binary Value");
}
}
}
public UByte(String hex) {
if (hex.length() <= 2 && hex.length() > 0) {
char[] hexChars = hex.toCharArray();
int intValue = 0;
for (int i = hexChars.length - 1; i >= 0; i--) {
switch (hexChars[i]) {
case '0' -> intValue += 0;
case '1' -> intValue += Math.abs(16*(i-1));
case '2' -> intValue += 2 * Math.abs(16*(i-1));
case '3' -> intValue += 3 * Math.abs(16*(i-1));
case '4' -> intValue += 4 * Math.abs(16*(i-1));
case '5' -> intValue += 5 * Math.abs(16*(i-1));
case '6' -> intValue += 6 * Math.abs(16*(i-1));
case '7' -> intValue += 7 * Math.abs(16*(i-1));
case '8' -> intValue += 8 * Math.abs(16*(i-1));
case '9' -> intValue += 9 * Math.abs(16*(i-1));
case 'A', 'a' -> intValue += 10 * Math.abs(16*(i-1));
case 'B', 'b' -> intValue += 11 * Math.abs(16*(i-1));
case 'C', 'c' -> intValue += 12 * Math.abs(16*(i-1));
case 'D', 'd' -> intValue += 13 * Math.abs(16*(i-1));
case 'E', 'e' -> intValue += 14 * Math.abs(16*(i-1));
case 'F', 'f' -> intValue += 15 * Math.abs(16*(i-1));
default -> throw new NumberFormatException("Invalid Hex Character at index " + i);
}
}
VALUE = intValue;
} else {
VALUE = 0;
throw new NumberFormatException("Invalid Hex Byte");
}
}
public UByte(byte byteValue) {
if ((int) byteValue < 0) {
VALUE = 256 + (int) byteValue;
} else {
VALUE = byteValue;
}
}
public int toBinary() {
int remainingValue = VALUE;
StringBuilder sb = new StringBuilder();
for (int i = 128; i > 0; i = i / 2) {
if (remainingValue >= i) {
sb.append("1");
remainingValue -= i;
} else {
sb.append("0");
}
}
return Integer.parseInt(sb.toString());
}
public String toHex() {
int value = VALUE;
int remainder = value;
StringBuilder sb = new StringBuilder();
while (remainder >= 0) {
if (remainder < 16) {
sb.append(DEC_HEX_MAP.get(remainder));
break;
} else {
int temp_remainder = remainder % 16;
int quotient = (remainder - temp_remainder) / 16;
sb.append(DEC_HEX_MAP.get(temp_remainder));
remainder = quotient;
}
}
if (sb.length() < 2) {
sb.append("0");
}
return sb.reverse().toString();
}
public int toInt() {
return VALUE;
}
public byte toByte() {
return (byte) VALUE;
}
public static UByte fromBinary(int binary) {
return new UByte(binary, false);
}
public static UByte fromHex(String hex) {
return new UByte(hex);
}
public static UByte fromInt(int integer) {
return new UByte(integer, true);
}
public static UByte fromByte(byte byteValue) {
return new UByte(byteValue);
}
public static UByte fromFlags(boolean flagOne, boolean flagTwo, boolean flagThree, boolean flagFour,
boolean flagFive, boolean flagSix, boolean flagSeven, boolean flagEight) {
StringBuilder sb = new StringBuilder();
sb.append(flagEight ? "1" : "0");
sb.append(flagSeven ? "1" : "0");
sb.append(flagSix ? "1" : "0");
sb.append(flagFive ? "1" : "0");
sb.append(flagFour ? "1" : "0");
sb.append(flagThree ? "1" : "0");
sb.append(flagTwo ? "1" : "0");
sb.append(flagOne ? "1" : "0");
return new UByte(Integer.parseInt(sb.toString()), false);
}
public String toString(byte b) {
return Integer.toString(b);
}
public String toString() {
return String.valueOf(VALUE);
}
@Override
public byte byteValue(){
return (byte) VALUE;
}
@Override
public int intValue() {
return VALUE;
}
@Override
public long longValue() {
return VALUE;
}
@Override
public float floatValue() {
return VALUE;
}
@Override
public double doubleValue() {
return VALUE;
}
}