Bug fixes
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>Protocol</artifactId>
|
<artifactId>Protocol</artifactId>
|
||||||
<version>1.0.0-BETA.7.6</version>
|
<version>1.0.0-BETA.7.7</version>
|
||||||
<organization>
|
<organization>
|
||||||
<name>Open Autonomous Connection</name>
|
<name>Open Autonomous Connection</name>
|
||||||
<url>https://open-autonomous-connection.org/</url>
|
<url>https://open-autonomous-connection.org/</url>
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.unlegitdqrk</groupId>
|
<groupId>dev.unlegitdqrk</groupId>
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.8.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
|
|||||||
@@ -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.INSRecord;
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -60,7 +61,11 @@ public final class CustomServerListener extends EventListener {
|
|||||||
if (!server.getProtocolBridge().isRunningAsWebServer()) return;
|
if (!server.getProtocolBridge().isRunningAsWebServer()) return;
|
||||||
if (!(event.getPacket() instanceof WebRequestPacket packet)) 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -129,11 +129,9 @@ public final class AuthPacket extends OACPacket {
|
|||||||
setResponseCode(INSResponseStatus.RESPONSE_AUTH_SUCCESS);
|
setResponseCode(INSResponseStatus.RESPONSE_AUTH_SUCCESS);
|
||||||
|
|
||||||
CustomConnectedClient client = protocolBridge.getProtocolServer().getClientByID(clientID);
|
CustomConnectedClient client = protocolBridge.getProtocolServer().getClientByID(clientID);
|
||||||
if (client != null) {
|
|
||||||
client.setClientVersion(clientVersion);
|
client.setClientVersion(clientVersion);
|
||||||
protocolBridge.getProtocolValues().eventManager.executeEvent(new S_CustomClientConnectedEvent(client));
|
protocolBridge.getProtocolValues().eventManager.executeEvent(new S_CustomClientConnectedEvent(client));
|
||||||
client.getConnection().sendPacket(new AuthPacket(protocolBridge), TransportProtocol.TCP);
|
client.getConnection().sendPacket(new AuthPacket(protocolBridge), TransportProtocol.TCP);
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -142,7 +140,12 @@ public final class AuthPacket extends OACPacket {
|
|||||||
boolean fromINS = objectInputStream.readBoolean();
|
boolean fromINS = objectInputStream.readBoolean();
|
||||||
ProtocolVersion serverVersion = ProtocolVersion.valueOf(objectInputStream.readUTF());
|
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)) {
|
if (!protocolBridge.isVersionSupported(serverVersion)) {
|
||||||
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
||||||
if (protocolBridge.getProtocolClient() != null && protocolBridge.getProtocolClient().getClientINSConnection() != null) {
|
if (protocolBridge.getProtocolClient() != null && protocolBridge.getProtocolClient().getClientINSConnection() != null) {
|
||||||
@@ -159,7 +162,10 @@ public final class AuthPacket extends OACPacket {
|
|||||||
|
|
||||||
if (caPem.equalsIgnoreCase("N/A")) {
|
if (caPem.equalsIgnoreCase("N/A")) {
|
||||||
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
||||||
} else {
|
protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] caBytes = caPem.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
byte[] caBytes = caPem.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
String fp = "N/A";
|
String fp = "N/A";
|
||||||
|
|
||||||
@@ -178,27 +184,24 @@ public final class AuthPacket extends OACPacket {
|
|||||||
protocolBridge.getProtocolClient().getFolderStructure().publicCAFolder,
|
protocolBridge.getProtocolClient().getFolderStructure().publicCAFolder,
|
||||||
caPrefix + ".fp");
|
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();
|
String existing = FileUtils.readFileLines(fpFile).getFirst();
|
||||||
if (!existing.equalsIgnoreCase(fp)) {
|
if (!existing.equalsIgnoreCase(fp)) {
|
||||||
if (!protocolBridge.getProtocolClient().trustNewINSFingerprint(existing, fp)) {
|
if (!protocolBridge.getProtocolClient().trustNewINSFingerprint(existing, fp)) {
|
||||||
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
||||||
protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
|
protocolBridge.getProtocolClient().getClientINSConnection().disconnect();
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileUtils.writeFile(fpFile, fp + System.lineSeparator());
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileUtils.writeFile(caPemFile, caPem);
|
FileUtils.writeFile(caPemFile, caPem);
|
||||||
@@ -206,17 +209,11 @@ public final class AuthPacket extends OACPacket {
|
|||||||
protocolBridge.getLogger().exception("Failed to create/save ca-files", exception);
|
protocolBridge.getLogger().exception("Failed to create/save ca-files", exception);
|
||||||
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protocolBridge.getProtocolClient().setInsVersion(serverVersion);
|
protocolBridge.getProtocolClient().setInsVersion(serverVersion);
|
||||||
protocolBridge.getProtocolValues().eventManager.executeEvent(
|
protocolBridge.getProtocolValues().eventManager.executeEvent(
|
||||||
new ConnectedToProtocolINSServerEvent(protocolBridge.getProtocolClient())
|
new ConnectedToProtocolINSServerEvent(protocolBridge.getProtocolClient())
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
protocolBridge.getProtocolClient().setServerVersion(serverVersion);
|
|
||||||
protocolBridge.getProtocolValues().eventManager.executeEvent(
|
|
||||||
new ConnectedToProtocolServerEvent(protocolBridge.getProtocolClient())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public abstract class ProtocolINSServer extends ProtocolCustomServer {
|
|||||||
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
|
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
|
||||||
String fp = java.util.HexFormat.of().formatHex(md.digest(caBytes));
|
String fp = java.util.HexFormat.of().formatHex(md.digest(caBytes));
|
||||||
|
|
||||||
System.out.println("CA Fingerprint: " + fp);
|
getProtocolBridge().getLogger().info("CA Fingerprint: " + fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -120,36 +120,75 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
trustStore.setCertificateEntry("root-ca-" + (caIndex++), caCert);
|
trustStore.setCertificateEntry("root-ca-" + (caIndex++), caCert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Build server key material (private key + certificate chain) ---
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
keyStore.load(null, null);
|
keyStore.load(null, null);
|
||||||
|
|
||||||
Map<String, File> keyFiles = new HashMap<>();
|
Map<String, File> keyFiles = new HashMap<>();
|
||||||
Map<String, File> certFiles = new HashMap<>();
|
Map<String, File> 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().endsWith(".srl")) continue;
|
||||||
if (!f.getName().startsWith(folderStructure.getCertPrefix())) continue;
|
if (!f.getName().startsWith(folderStructure.getCertPrefix())) continue;
|
||||||
|
keyFiles.put(FileUtils.stripExt(f.getName()), f);
|
||||||
|
}
|
||||||
|
|
||||||
String base = FileUtils.stripExt(f.getName());
|
// Allow cert files in public server folder as .pem/.crt/.cer
|
||||||
if (f.getName().endsWith(".key")) keyFiles.put(base, f);
|
for (File f : FileUtils.listFiles(folderStructure.publicServerFolder, ".pem", ".crt", ".cer")) {
|
||||||
if (f.getName().endsWith(".pem")) certFiles.put(base, f);
|
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<X509Certificate> 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;
|
int serverIndex = 0;
|
||||||
for (String base : keyFiles.keySet()) {
|
for (Map.Entry<String, File> e : keyFiles.entrySet()) {
|
||||||
File keyFile = keyFiles.get(base);
|
String base = e.getKey();
|
||||||
|
File keyFile = e.getValue();
|
||||||
|
|
||||||
File certFile = certFiles.get(base);
|
File certFile = certFiles.get(base);
|
||||||
if (certFile == null) {
|
if (certFile == null) {
|
||||||
File alt = new File(folderStructure.publicServerFolder, base + ".pem");
|
// If your naming differs between private key and public cert, log it and skip
|
||||||
if (alt.exists()) certFile = alt;
|
continue;
|
||||||
}
|
}
|
||||||
if (certFile == null) continue;
|
|
||||||
|
|
||||||
PrivateKey key = PemUtils.loadPrivateKey(keyFile);
|
PrivateKey privateKey = PemUtils.loadPrivateKey(keyFile);
|
||||||
X509Certificate cert = PemUtils.loadCertificate(certFile);
|
X509Certificate leaf = PemUtils.loadCertificate(certFile);
|
||||||
|
|
||||||
|
// Build a minimal chain: leaf + issuer CA if found (or all CAs as fallback)
|
||||||
|
List<X509Certificate> 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++);
|
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()
|
network = new NetworkServer.Builder()
|
||||||
@@ -190,6 +229,22 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
return null;
|
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<X509Certificate> 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.
|
* 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.
|
* @throws IOException If an I/O error occurs while checking the files.
|
||||||
*/
|
*/
|
||||||
private void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException {
|
private void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException {
|
||||||
boolean found = false;
|
|
||||||
|
|
||||||
// Ensure the folder exists
|
// Ensure the folder exists
|
||||||
if (folder == null) throw new FileNotFoundException("Folder does not exist");
|
if (folder == null) throw new FileNotFoundException("Folder does not exist");
|
||||||
|
|
||||||
@@ -214,13 +267,7 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
if (!file.getName().startsWith(prefix))
|
if (!file.getName().startsWith(prefix))
|
||||||
throw new CertificateException(file.getAbsolutePath() + " is not valid");
|
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)
|
@Listener(priority = EventPriority.LOW)
|
||||||
|
|||||||
Reference in New Issue
Block a user