diff --git a/pom.xml b/pom.xml
index 2e24596..426fec9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.openautonomousconnection
Protocol
- 1.0.0-BETA.7.6
+ 1.0.0-BETA.7.7
Open Autonomous Connection
https://open-autonomous-connection.org/
@@ -119,7 +119,7 @@
dev.unlegitdqrk
unlegitlibrary
- 1.8.0
+ 1.8.1
org.projectlombok
diff --git a/src/main/java/org/openautonomousconnection/protocol/listeners/CustomServerListener.java b/src/main/java/org/openautonomousconnection/protocol/listeners/CustomServerListener.java
index 635573b..395b5bb 100644
--- a/src/main/java/org/openautonomousconnection/protocol/listeners/CustomServerListener.java
+++ b/src/main/java/org/openautonomousconnection/protocol/listeners/CustomServerListener.java
@@ -16,6 +16,7 @@ import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -60,7 +61,11 @@ public final class CustomServerListener extends EventListener {
if (!server.getProtocolBridge().isRunningAsWebServer()) return;
if (!(event.getPacket() instanceof WebRequestPacket packet)) return;
- ((ProtocolWebServer) server).onWebRequest(server.getClientByID(event.getClient().getUniqueID()), packet);
+ try {
+ event.getClient().sendPacket(((ProtocolWebServer) server).onWebRequest(server.getClientByID(event.getClient().getUniqueID()), packet), TransportProtocol.TCP);
+ } catch (IOException e) {
+ server.getProtocolBridge().getLogger().exception("Failed to send web response to client", e);
+ }
}
/**
diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java
index 0793dad..c847cdc 100644
--- a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java
+++ b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java
@@ -129,11 +129,9 @@ public final class AuthPacket extends OACPacket {
setResponseCode(INSResponseStatus.RESPONSE_AUTH_SUCCESS);
CustomConnectedClient client = protocolBridge.getProtocolServer().getClientByID(clientID);
- if (client != null) {
- client.setClientVersion(clientVersion);
- protocolBridge.getProtocolValues().eventManager.executeEvent(new S_CustomClientConnectedEvent(client));
- client.getConnection().sendPacket(new AuthPacket(protocolBridge), TransportProtocol.TCP);
- }
+ client.setClientVersion(clientVersion);
+ protocolBridge.getProtocolValues().eventManager.executeEvent(new S_CustomClientConnectedEvent(client));
+ client.getConnection().sendPacket(new AuthPacket(protocolBridge), TransportProtocol.TCP);
return;
}
@@ -142,7 +140,12 @@ public final class AuthPacket extends OACPacket {
boolean fromINS = objectInputStream.readBoolean();
ProtocolVersion serverVersion = ProtocolVersion.valueOf(objectInputStream.readUTF());
- if (fromINS) {
+ if (!fromINS) {
+ protocolBridge.getProtocolClient().setServerVersion(serverVersion);
+ protocolBridge.getProtocolValues().eventManager.executeEvent(
+ new ConnectedToProtocolServerEvent(protocolBridge.getProtocolClient())
+ );
+ } else {
if (!protocolBridge.isVersionSupported(serverVersion)) {
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
if (protocolBridge.getProtocolClient() != null && protocolBridge.getProtocolClient().getClientINSConnection() != null) {
@@ -159,7 +162,10 @@ public final class AuthPacket extends OACPacket {
if (caPem.equalsIgnoreCase("N/A")) {
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
- } else {
+ protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
+ return;
+ }
+
byte[] caBytes = caPem.getBytes(java.nio.charset.StandardCharsets.UTF_8);
String fp = "N/A";
@@ -178,45 +184,36 @@ public final class AuthPacket extends OACPacket {
protocolBridge.getProtocolClient().getFolderStructure().publicCAFolder,
caPrefix + ".fp");
-
- if (fpFile.exists()) {
+ if (!fpFile.exists()) {
+ if (!protocolBridge.getProtocolClient().trustINS(fp)) {
+ setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
+ protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
+ return;
+ }
+ } else {
String existing = FileUtils.readFileLines(fpFile).getFirst();
if (!existing.equalsIgnoreCase(fp)) {
if (!protocolBridge.getProtocolClient().trustNewINSFingerprint(existing, fp)) {
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
return;
- } else {
- FileUtils.writeFile(fpFile, fp + System.lineSeparator());
}
}
- } else {
- if (!protocolBridge.getProtocolClient().trustINS(fp)) {
- setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
- protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
- return;
- } else {
- FileUtils.writeFile(fpFile, fp + System.lineSeparator());
- }
}
+ FileUtils.writeFile(fpFile, fp + System.lineSeparator());
+
try {
FileUtils.writeFile(caPemFile, caPem);
} catch (Exception exception) {
protocolBridge.getLogger().exception("Failed to create/save ca-files", exception);
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
}
- }
protocolBridge.getProtocolClient().setInsVersion(serverVersion);
protocolBridge.getProtocolValues().eventManager.executeEvent(
new ConnectedToProtocolINSServerEvent(protocolBridge.getProtocolClient())
);
- } else {
- protocolBridge.getProtocolClient().setServerVersion(serverVersion);
- protocolBridge.getProtocolValues().eventManager.executeEvent(
- new ConnectedToProtocolServerEvent(protocolBridge.getProtocolClient())
- );
}
}
}
diff --git a/src/main/java/org/openautonomousconnection/protocol/side/ins/ProtocolINSServer.java b/src/main/java/org/openautonomousconnection/protocol/side/ins/ProtocolINSServer.java
index 245534d..2f70fe3 100644
--- a/src/main/java/org/openautonomousconnection/protocol/side/ins/ProtocolINSServer.java
+++ b/src/main/java/org/openautonomousconnection/protocol/side/ins/ProtocolINSServer.java
@@ -65,7 +65,7 @@ public abstract class ProtocolINSServer extends ProtocolCustomServer {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
String fp = java.util.HexFormat.of().formatHex(md.digest(caBytes));
- System.out.println("CA Fingerprint: " + fp);
+ getProtocolBridge().getLogger().info("CA Fingerprint: " + fp);
}
/**
diff --git a/src/main/java/org/openautonomousconnection/protocol/side/server/ProtocolCustomServer.java b/src/main/java/org/openautonomousconnection/protocol/side/server/ProtocolCustomServer.java
index 5cf4ad7..9cc8638 100644
--- a/src/main/java/org/openautonomousconnection/protocol/side/server/ProtocolCustomServer.java
+++ b/src/main/java/org/openautonomousconnection/protocol/side/server/ProtocolCustomServer.java
@@ -120,36 +120,75 @@ public abstract class ProtocolCustomServer extends EventListener {
trustStore.setCertificateEntry("root-ca-" + (caIndex++), caCert);
}
+ // --- Build server key material (private key + certificate chain) ---
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
Map keyFiles = new HashMap<>();
Map certFiles = new HashMap<>();
- for (File f : FileUtils.listFiles(folderStructure.privateServerFolder, ".key", ".pem")) {
+ for (File f : FileUtils.listFiles(folderStructure.privateServerFolder, ".key")) {
if (f.getName().endsWith(".srl")) continue;
if (!f.getName().startsWith(folderStructure.getCertPrefix())) continue;
+ keyFiles.put(FileUtils.stripExt(f.getName()), f);
+ }
- String base = FileUtils.stripExt(f.getName());
- if (f.getName().endsWith(".key")) keyFiles.put(base, f);
- if (f.getName().endsWith(".pem")) certFiles.put(base, f);
+// Allow cert files in public server folder as .pem/.crt/.cer
+ for (File f : FileUtils.listFiles(folderStructure.publicServerFolder, ".pem", ".crt", ".cer")) {
+ if (f.getName().endsWith(".srl")) continue;
+ if (!f.getName().startsWith(folderStructure.getCertPrefix())) continue;
+ certFiles.put(FileUtils.stripExt(f.getName()), f);
+ }
+
+// Load all CA certs once (for chain building)
+ List caCerts = new ArrayList<>();
+ for (File caPem : FileUtils.listFiles(folderStructure.publicCAFolder, ".pem", ".crt", ".cer")) {
+ if (caPem.getName().endsWith(".srl")) continue;
+ if (!caPem.getName().startsWith(folderStructure.getCaPrefix())) continue;
+ caCerts.add(PemUtils.loadCertificate(caPem));
}
int serverIndex = 0;
- for (String base : keyFiles.keySet()) {
- File keyFile = keyFiles.get(base);
+ for (Map.Entry e : keyFiles.entrySet()) {
+ String base = e.getKey();
+ File keyFile = e.getValue();
+
File certFile = certFiles.get(base);
if (certFile == null) {
- File alt = new File(folderStructure.publicServerFolder, base + ".pem");
- if (alt.exists()) certFile = alt;
+ // If your naming differs between private key and public cert, log it and skip
+ continue;
}
- if (certFile == null) continue;
- PrivateKey key = PemUtils.loadPrivateKey(keyFile);
- X509Certificate cert = PemUtils.loadCertificate(certFile);
+ PrivateKey privateKey = PemUtils.loadPrivateKey(keyFile);
+ X509Certificate leaf = PemUtils.loadCertificate(certFile);
+
+ // Build a minimal chain: leaf + issuer CA if found (or all CAs as fallback)
+ List chain = new ArrayList<>();
+ chain.add(leaf);
+
+ // Try to find matching issuer CA(s) by Subject/Issuer DN
+ X509Certificate current = leaf;
+ for (int depth = 0; depth < 5; depth++) {
+ X509Certificate issuer = findIssuer(current, caCerts);
+ if (issuer == null) break;
+ chain.add(issuer);
+ if (issuer.getSubjectX500Principal().equals(issuer.getIssuerX500Principal())) {
+ // self-signed root reached
+ break;
+ }
+ current = issuer;
+ }
String alias = "server-" + (serverIndex++);
- keyStore.setKeyEntry(alias, key, keyPass, new X509Certificate[]{cert});
+ keyStore.setKeyEntry(alias, privateKey, keyPass, chain.toArray(new X509Certificate[0]));
+ }
+
+// If still empty, fail fast with a clear error instead of handshake_failure later
+ if (!keyStore.aliases().hasMoreElements()) {
+ throw new IllegalStateException(
+ "No server key entries loaded. Ensure private .key and public .crt/.pem names match and start with prefix "
+ + folderStructure.getCertPrefix()
+ );
}
network = new NetworkServer.Builder()
@@ -190,6 +229,22 @@ public abstract class ProtocolCustomServer extends EventListener {
return null;
}
+ /**
+ * Attempts to find the issuer certificate of {@code cert} within {@code candidates}.
+ *
+ * @param cert the certificate whose issuer should be found
+ * @param candidates possible issuer certificates
+ * @return issuer certificate if found, otherwise null
+ */
+ private X509Certificate findIssuer(X509Certificate cert, List candidates) {
+ for (X509Certificate ca : candidates) {
+ if (cert.getIssuerX500Principal().equals(ca.getSubjectX500Principal())) {
+ return ca;
+ }
+ }
+ return null;
+ }
+
/**
* Checks if the required certificate files exist in the specified folder.
*
@@ -200,8 +255,6 @@ public abstract class ProtocolCustomServer extends EventListener {
* @throws IOException If an I/O error occurs while checking the files.
*/
private void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException {
- boolean found = false;
-
// Ensure the folder exists
if (folder == null) throw new FileNotFoundException("Folder does not exist");
@@ -214,13 +267,7 @@ public abstract class ProtocolCustomServer extends EventListener {
for (File file : files) {
if (!file.getName().startsWith(prefix))
throw new CertificateException(file.getAbsolutePath() + " is not valid");
-
- // Check for file matching the public IP address
- if (!found) found = file.getName().equalsIgnoreCase(prefix + NetworkUtils.getPublicIPAddress() + extension);
}
-
- // Throw exception if the required file is not found
- if (!found) throw new CertificateException("Missing " + prefix + NetworkUtils.getPublicIPAddress() + extension);
}
@Listener(priority = EventPriority.LOW)