From 23a3293060d7c3cdc12d55ea5efbe0b29d94fcce Mon Sep 17 00:00:00 2001 From: UnlegitDqrk Date: Sun, 8 Feb 2026 22:01:19 +0100 Subject: [PATCH] Bug fixes --- pom.xml | 4 +- .../listeners/CustomServerListener.java | 7 +- .../packets/v1_0_0/beta/AuthPacket.java | 47 +++++----- .../protocol/side/ins/ProtocolINSServer.java | 2 +- .../side/server/ProtocolCustomServer.java | 87 ++++++++++++++----- 5 files changed, 98 insertions(+), 49 deletions(-) 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)