From 6afc26e60194e932fd372141159c5f8a0d15923c Mon Sep 17 00:00:00 2001 From: Sen Date: Mon, 16 Jun 2025 15:12:39 +0200 Subject: [PATCH] change key exchange to ED25519 --- .../src/main/java/client/gui/GuiServer.java | 10 +- .../client/network/ClientLoginHandler.java | 29 +++-- .../java/common/packet/LPacketChallenge.java | 12 +-- .../java/common/packet/LPacketResponse.java | 12 ++- .../common/packet/LPacketStartEncrypt.java | 25 ++--- .../java/common/packet/RPacketChallenge.java | 12 +-- .../common/packet/RPacketRequestEncrypt.java | 14 +-- .../java/common/packet/RPacketResponse.java | 12 ++- .../main/java/common/util/EncryptUtil.java | 101 ++++++++++++++---- server/src/main/java/server/Server.java | 2 +- .../java/server/network/LoginHandler.java | 24 ++--- 11 files changed, 148 insertions(+), 105 deletions(-) diff --git a/client/src/main/java/client/gui/GuiServer.java b/client/src/main/java/client/gui/GuiServer.java index 856c189..a15b796 100644 --- a/client/src/main/java/client/gui/GuiServer.java +++ b/client/src/main/java/client/gui/GuiServer.java @@ -97,7 +97,7 @@ public class GuiServer extends Gui implements FieldCallback { if(GuiServer.this.keypair == null) { GuiServer.this.keypair = EncryptUtil.createKeypair(); GuiServer.this.keyDigest = EncryptUtil.getXorSha512Hash(GuiServer.this.keypair.getPublic().getEncoded()); - GuiServer.this.keyLabel.setText("Anmelde-Pubkey: RSA-4096 " + GuiServer.this.keyDigest); + GuiServer.this.keyLabel.setText("Anmelde-Pubkey: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.keyDigest); GuiServer.this.keyButton.setText("Schlüsselpaar entfernen"); GuiServer.this.passLabel.setText("Ersatz-Passwort (mind. 8 Zeichen)"); GuiServer.this.copyKeyButton.enabled = true; @@ -124,7 +124,7 @@ public class GuiServer extends Gui implements FieldCallback { GuiServer.this.encToggle.setValue(reqEnc); GuiServer.this.serverKey = key; GuiServer.this.serverDigest = sdigest; - GuiServer.this.idLabel.setText("Server-Pubkey: " + (key != null ? "RSA-4096 " + GuiServer.this.serverDigest : "nicht vorhanden")); + GuiServer.this.idLabel.setText("Server-Pubkey: " + (key != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.serverDigest : "nicht vorhanden")); GuiServer.this.resetButton.enabled = key != null; GuiServer.this.copyIdButton.enabled = key != null; GuiServer.this.copyKeyButton.enabled = !confirmed; @@ -138,7 +138,7 @@ public class GuiServer extends Gui implements FieldCallback { else { GuiServer.this.keypair = keys; GuiServer.this.keyDigest = digest; - GuiServer.this.keyLabel.setText("Anmelde-Pubkey: RSA-4096 " + GuiServer.this.keyDigest); + GuiServer.this.keyLabel.setText("Anmelde-Pubkey: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.keyDigest); GuiServer.this.keyButton.setText("Schlüsselpaar entfernen"); GuiServer.this.passLabel.setText("Ersatz-Passwort (mind. 8 Zeichen)"); } @@ -154,7 +154,7 @@ public class GuiServer extends Gui implements FieldCallback { } }, "Kopieren")); this.copyKeyButton.enabled = this.keypair != null; - this.keyLabel = this.add(new Label(0, 102, 480, "Anmelde-Pubkey: " + (this.keypair != null ? "RSA-4096 " + this.keyDigest : "nicht vorhanden"), true)); + this.keyLabel = this.add(new Label(0, 102, 480, "Anmelde-Pubkey: " + (this.keypair != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + this.keyDigest : "nicht vorhanden"), true)); this.encToggle = this.add(new Toggle(0, 190, 480, 0, false, this.server.requiresEncryption(), null, "Nur Verschlüsselte Verbindung akzeptieren")); this.serverKey = this.server.getServerKey(); this.serverDigest = this.serverKey == null ? null : EncryptUtil.getXorSha512Hash(this.serverKey.getEncoded()); @@ -177,7 +177,7 @@ public class GuiServer extends Gui implements FieldCallback { } }, "Kopieren")); this.copyIdButton.enabled = this.serverKey != null; - this.idLabel = this.add(new Label(0, 224, 480, "Server-Pubkey: " + (this.serverKey != null ? "RSA-4096 " + this.serverDigest : "nicht vorhanden"), true)); + this.idLabel = this.add(new Label(0, 224, 480, "Server-Pubkey: " + (this.serverKey != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + this.serverDigest : "nicht vorhanden"), true)); } this.shift(); } diff --git a/client/src/main/java/client/network/ClientLoginHandler.java b/client/src/main/java/client/network/ClientLoginHandler.java index 28d7269..0509522 100755 --- a/client/src/main/java/client/network/ClientLoginHandler.java +++ b/client/src/main/java/client/network/ClientLoginHandler.java @@ -1,12 +1,10 @@ package client.network; +import java.security.KeyPair; import java.security.PublicKey; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Base64; -import javax.crypto.SecretKey; - import client.Client; import client.gui.GuiConfirm; import client.gui.GuiConnect; @@ -41,6 +39,7 @@ public class ClientLoginHandler implements IClientLoginHandler { private final Client gm; private final NetConnection connection; private final ServerInfo server; + private final KeyPair tempKeys; private LoginState state = LoginState.HANDSHAKE; private byte[] token; @@ -49,6 +48,7 @@ public class ClientLoginHandler implements IClientLoginHandler { this.connection = conn; this.gm = gm; this.server = server; + this.tempKeys = EncryptUtil.createDHKeypair(); } public void onDisconnect(String reason) @@ -67,9 +67,8 @@ public class ClientLoginHandler implements IClientLoginHandler { return; } this.connection.setConnectionState(PacketRegistry.LOGIN); - final SecretKey secret = EncryptUtil.createSharedKey(); final PublicKey pubkey = packet.getKey(); - final byte[] token = packet.getToken(); + final PublicKey tempKey = packet.getTempKey(); if(this.server.getServerKey() == null) { this.state = LoginState.CONFIRMING; this.gm.schedule(new Runnable() { @@ -80,33 +79,33 @@ public class ClientLoginHandler implements IClientLoginHandler { ClientLoginHandler.this.server.setServerKey(pubkey); GuiConnect.INSTANCE.editServer(ClientLoginHandler.this.server); ClientLoginHandler.this.gm.displayConnecting(ClientLoginHandler.this.server); - ClientLoginHandler.this.startEncryption(secret, pubkey, token); + ClientLoginHandler.this.startEncryption(tempKey); } else { ClientLoginHandler.this.connection.closeChannel("Verbindung wurde abgebrochen"); } } - }, "Die Identität des Servers ist unbekannt", "Es wurde noch nie mit diesem Server verbunden.\nSoll die Verbindung wirklich fortgesetzt werden?\n\nDie Pubkey-ID des Servers lautet:\nRSA-4096 " + EncryptUtil.getXorSha512Hash(pubkey.getEncoded()) + "\n\nDer öffentliche Schlüssel des Servers lautet:\n" + Util.breakString(Base64.getEncoder().encodeToString(pubkey.getEncoded()), 64), "Verbindung herstellen", "Abbrechen und trennen")); + }, "Die Identität des Servers ist unbekannt", "Es wurde noch nie mit diesem Server verbunden.\nSoll die Verbindung wirklich fortgesetzt werden?\n\nDie Pubkey-ID des Servers lautet:\n" + EncryptUtil.KEY_ALGO_DISPLAY + " " + EncryptUtil.getXorSha512Hash(pubkey.getEncoded()) + "\n\nDer öffentliche Schlüssel des Servers lautet:\n" + Util.breakString(Base64.getEncoder().encodeToString(pubkey.getEncoded()), 64), "Verbindung herstellen", "Abbrechen und trennen")); } }); return; } else if(!this.server.getServerKey().equals(pubkey)) { - this.connection.closeChannel("Die Identität des Servers hat sich geändert\n\nDer Server hat einen anderen öffentlichen Schlüssel als vorher bekannt war,\ndies kann bedeuten dass der Inhaber jetzt einen anderen Schlüssel verwendet\noder sich ein anderer Server als dieser ausgibt (\"Man-in-the-middle-attack\").\n\nDie Pubkey-ID des Servers lautet: RSA-4096 " + EncryptUtil.getXorSha512Hash(pubkey.getEncoded()) + "\n\nDer öffentliche Schlüssel des Servers lautet:\n" + Util.breakString(Base64.getEncoder().encodeToString(pubkey.getEncoded()), 64) + "\n\nDie vertrauenswürdige Pubkey-ID lautet: RSA-4096 " + EncryptUtil.getXorSha512Hash(this.server.getServerKey().getEncoded()) + + this.connection.closeChannel("Die Identität des Servers hat sich geändert\n\nDer Server hat einen anderen öffentlichen Schlüssel als vorher bekannt war,\ndies kann bedeuten dass der Inhaber jetzt einen anderen Schlüssel verwendet\noder sich ein anderer Server als dieser ausgibt (\"Man-in-the-middle-attack\").\n\nDie Pubkey-ID des Servers lautet: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + EncryptUtil.getXorSha512Hash(pubkey.getEncoded()) + "\n\nDer öffentliche Schlüssel des Servers lautet:\n" + Util.breakString(Base64.getEncoder().encodeToString(pubkey.getEncoded()), 64) + "\n\nDie vertrauenswürdige Pubkey-ID lautet: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + EncryptUtil.getXorSha512Hash(this.server.getServerKey().getEncoded()) + "\n\nFalls der Server trotzdem vertrauenswürdig wirkt, kann die Server-Identifizierung\nin den Einstellungen von '" + this.server.getName() + "' zurückgesetzt werden."); return; } - this.startEncryption(secret, pubkey, token); + this.startEncryption(tempKey); } - private void startEncryption(SecretKey secret, PublicKey pubkey, byte[] token) { + private void startEncryption(PublicKey serverKey) { this.state = LoginState.CHALLENGE; - this.connection.sendPacket(new LPacketStartEncrypt(secret, pubkey, token), new GenericFutureListener < Future > () { + this.connection.sendPacket(new LPacketStartEncrypt(this.tempKeys.getPublic()), new GenericFutureListener < Future > () { public void operationComplete(Future u) throws Exception { - ClientLoginHandler.this.connection.startEncryption(secret); + ClientLoginHandler.this.connection.startEncryption(EncryptUtil.makeKeyAgreement(ClientLoginHandler.this.tempKeys.getPrivate(), serverKey)); ClientLoginHandler.this.token = new byte[32]; TOKEN_RNG.nextBytes(ClientLoginHandler.this.token); - ClientLoginHandler.this.connection.sendPacket(new LPacketChallenge(pubkey, ClientLoginHandler.this.token)); + ClientLoginHandler.this.connection.sendPacket(new LPacketChallenge(ClientLoginHandler.this.token)); } }); } @@ -116,7 +115,7 @@ public class ClientLoginHandler implements IClientLoginHandler { this.connection.closeChannel("Unerwartetes Beweis-Paket"); return; } - if(!Arrays.equals(this.token, packet.getToken())) { + if(!packet.verifyToken(this.server.getServerKey(), this.token)) { this.connection.closeChannel("Fehlerhaftes Beweis-Token"); return; } @@ -189,7 +188,7 @@ public class ClientLoginHandler implements IClientLoginHandler { return; } this.state = LoginState.AUTHENTICATING; - this.connection.sendPacket(new LPacketResponse(packet.getToken(this.server.getKeypair().getPrivate()))); + this.connection.sendPacket(new LPacketResponse(this.server.getKeypair().getPrivate(), packet.getToken())); } public void handleLoginSuccess(RPacketLoginSuccess packet) diff --git a/common/src/main/java/common/packet/LPacketChallenge.java b/common/src/main/java/common/packet/LPacketChallenge.java index 469d87e..11fceec 100644 --- a/common/src/main/java/common/packet/LPacketChallenge.java +++ b/common/src/main/java/common/packet/LPacketChallenge.java @@ -1,13 +1,9 @@ package common.packet; import java.io.IOException; -import java.security.PrivateKey; -import java.security.PublicKey; - import common.network.ILoginHandler; import common.network.Packet; import common.network.PacketBuffer; -import common.util.EncryptUtil; public class LPacketChallenge implements Packet { private byte[] token; @@ -15,8 +11,8 @@ public class LPacketChallenge implements Packet { public LPacketChallenge() { } - public LPacketChallenge(PublicKey pubkey, byte[] token) { - this.token = EncryptUtil.encryptData(pubkey, token); + public LPacketChallenge(byte[] token) { + this.token = token; } public final void readPacketData(PacketBuffer buf) throws IOException { @@ -31,7 +27,7 @@ public class LPacketChallenge implements Packet { handler.processChallenge(this); } - public byte[] getToken(PrivateKey key) { - return EncryptUtil.decryptData(key, this.token); + public byte[] getToken() { + return this.token; } } diff --git a/common/src/main/java/common/packet/LPacketResponse.java b/common/src/main/java/common/packet/LPacketResponse.java index 7c27903..554bc1e 100644 --- a/common/src/main/java/common/packet/LPacketResponse.java +++ b/common/src/main/java/common/packet/LPacketResponse.java @@ -1,9 +1,13 @@ package common.packet; import java.io.IOException; +import java.security.PrivateKey; +import java.security.PublicKey; + import common.network.ILoginHandler; import common.network.Packet; import common.network.PacketBuffer; +import common.util.EncryptUtil; public class LPacketResponse implements Packet { private byte[] token = new byte[0]; @@ -11,8 +15,8 @@ public class LPacketResponse implements Packet { public LPacketResponse() { } - public LPacketResponse(byte[] token) { - this.token = token; + public LPacketResponse(PrivateKey key, byte[] token) { + this.token = EncryptUtil.createSignature(key, token); } public void readPacketData(PacketBuffer buf) throws IOException { @@ -27,7 +31,7 @@ public class LPacketResponse implements Packet { handler.processResponse(this); } - public byte[] getToken() { - return this.token; + public boolean verifyToken(PublicKey key, byte[] token) { + return EncryptUtil.verifySignature(key, token, this.token); } } diff --git a/common/src/main/java/common/packet/LPacketStartEncrypt.java b/common/src/main/java/common/packet/LPacketStartEncrypt.java index fb66fc0..b6d4995 100644 --- a/common/src/main/java/common/packet/LPacketStartEncrypt.java +++ b/common/src/main/java/common/packet/LPacketStartEncrypt.java @@ -1,47 +1,36 @@ package common.packet; import java.io.IOException; -import java.security.PrivateKey; import java.security.PublicKey; -import javax.crypto.SecretKey; - import common.network.ILoginHandler; import common.network.Packet; import common.network.PacketBuffer; import common.util.EncryptUtil; public class LPacketStartEncrypt implements Packet { - private byte[] key = new byte[0]; - private byte[] token = new byte[0]; + private PublicKey key; public LPacketStartEncrypt() { } - public LPacketStartEncrypt(SecretKey secret, PublicKey pubkey, byte[] token) { - this.key = EncryptUtil.encryptData(pubkey, secret.getEncoded()); - this.token = EncryptUtil.encryptData(pubkey, token); + public LPacketStartEncrypt(PublicKey pubkey) { + this.key = pubkey; } public void readPacketData(PacketBuffer buf) throws IOException { - this.key = buf.readByteArray(); - this.token = buf.readByteArray(); + this.key = EncryptUtil.decodeDHPublicKey(buf.readByteArray()); } public void writePacketData(PacketBuffer buf) throws IOException { - buf.writeByteArray(this.key); - buf.writeByteArray(this.token); + buf.writeByteArray(this.key.getEncoded()); } public void processPacket(ILoginHandler handler) { handler.processEncryption(this); } - public SecretKey getKey(PrivateKey key) { - return EncryptUtil.decryptSharedKey(key, this.key); - } - - public byte[] getToken(PrivateKey key) { - return EncryptUtil.decryptData(key, this.token); + public PublicKey getKey() { + return this.key; } } diff --git a/common/src/main/java/common/packet/RPacketChallenge.java b/common/src/main/java/common/packet/RPacketChallenge.java index d761d42..9ae9012 100644 --- a/common/src/main/java/common/packet/RPacketChallenge.java +++ b/common/src/main/java/common/packet/RPacketChallenge.java @@ -1,13 +1,9 @@ package common.packet; import java.io.IOException; -import java.security.PrivateKey; -import java.security.PublicKey; - import common.network.IClientLoginHandler; import common.network.Packet; import common.network.PacketBuffer; -import common.util.EncryptUtil; public class RPacketChallenge implements Packet { private byte[] token; @@ -15,8 +11,8 @@ public class RPacketChallenge implements Packet { public RPacketChallenge() { } - public RPacketChallenge(PublicKey pubkey, byte[] token) { - this.token = EncryptUtil.encryptData(pubkey, token); + public RPacketChallenge(byte[] token) { + this.token = token; } public final void readPacketData(PacketBuffer buf) throws IOException { @@ -31,7 +27,7 @@ public class RPacketChallenge implements Packet { handler.handleChallenge(this); } - public byte[] getToken(PrivateKey key) { - return EncryptUtil.decryptData(key, this.token); + public byte[] getToken() { + return this.token; } } diff --git a/common/src/main/java/common/packet/RPacketRequestEncrypt.java b/common/src/main/java/common/packet/RPacketRequestEncrypt.java index 2b45964..4492a17 100644 --- a/common/src/main/java/common/packet/RPacketRequestEncrypt.java +++ b/common/src/main/java/common/packet/RPacketRequestEncrypt.java @@ -10,24 +10,24 @@ import common.util.EncryptUtil; public class RPacketRequestEncrypt implements Packet { private PublicKey key; - private byte[] token; + private PublicKey tempKey; public RPacketRequestEncrypt() { } - public RPacketRequestEncrypt(PublicKey key, byte[] token) { + public RPacketRequestEncrypt(PublicKey key, PublicKey tempKey) { this.key = key; - this.token = token; + this.tempKey = tempKey; } public final void readPacketData(PacketBuffer buf) throws IOException { this.key = EncryptUtil.decodePublicKey(buf.readByteArray()); - this.token = buf.readByteArray(); + this.tempKey = EncryptUtil.decodeDHPublicKey(buf.readByteArray()); } public final void writePacketData(PacketBuffer buf) throws IOException { buf.writeByteArray(this.key.getEncoded()); - buf.writeByteArray(this.token); + buf.writeByteArray(this.tempKey.getEncoded()); } public void processPacket(IClientLoginHandler handler) { @@ -38,7 +38,7 @@ public class RPacketRequestEncrypt implements Packet { return this.key; } - public byte[] getToken() { - return this.token; + public PublicKey getTempKey() { + return this.tempKey; } } diff --git a/common/src/main/java/common/packet/RPacketResponse.java b/common/src/main/java/common/packet/RPacketResponse.java index abeac80..aa3e490 100644 --- a/common/src/main/java/common/packet/RPacketResponse.java +++ b/common/src/main/java/common/packet/RPacketResponse.java @@ -1,9 +1,13 @@ package common.packet; import java.io.IOException; +import java.security.PrivateKey; +import java.security.PublicKey; + import common.network.IClientLoginHandler; import common.network.Packet; import common.network.PacketBuffer; +import common.util.EncryptUtil; public class RPacketResponse implements Packet { private byte[] token = new byte[0]; @@ -11,8 +15,8 @@ public class RPacketResponse implements Packet { public RPacketResponse() { } - public RPacketResponse(byte[] token) { - this.token = token; + public RPacketResponse(PrivateKey key, byte[] token) { + this.token = EncryptUtil.createSignature(key, token); } public void readPacketData(PacketBuffer buf) throws IOException { @@ -27,7 +31,7 @@ public class RPacketResponse implements Packet { handler.handleResponse(this); } - public byte[] getToken() { - return this.token; + public boolean verifyToken(PublicKey key, byte[] token) { + return EncryptUtil.verifySignature(key, token, this.token); } } diff --git a/common/src/main/java/common/util/EncryptUtil.java b/common/src/main/java/common/util/EncryptUtil.java index 41d0b01..1db6de8 100644 --- a/common/src/main/java/common/util/EncryptUtil.java +++ b/common/src/main/java/common/util/EncryptUtil.java @@ -11,6 +11,8 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.EncodedKeySpec; import java.security.spec.InvalidKeySpecException; @@ -23,7 +25,7 @@ import java.util.Base64; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; +import javax.crypto.KeyAgreement; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; @@ -34,28 +36,93 @@ import javax.crypto.spec.SecretKeySpec; import common.log.Log; public class EncryptUtil { + public static final String KEY_ALGO_NAME = "tcr-ed25519"; + public static final String KEY_ALGO_DISPLAY = "Ed25519"; + private static final long CRC24_INIT = 0xB704CEL; private static final long CRC24_POLY = 0x1864CFBL; - public static SecretKey createSharedKey() { +// private static byte[] deriveKey(byte[] secret) { +// try { +// KeySpec spec = new PBEKeySpec +// SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); +// return factory.generateSecret(spec).getEncoded(); +// } +// catch(NoSuchAlgorithmException | InvalidKeySpecException e) { +// Log.SYSTEM.error(e, "Konnte Passwort-Prüfwert nicht berechnen"); +// return null; +// } +// } + + public static SecretKey makeKeyAgreement(PrivateKey key, PublicKey pubkey) { try { - KeyGenerator keygen = KeyGenerator.getInstance("AES"); - keygen.init(128); - return keygen.generateKey(); + KeyAgreement agreement = KeyAgreement.getInstance("X25519"); + agreement.init(key); + agreement.doPhase(pubkey, true); + return new SecretKeySpec(agreement.generateSecret(), 0, 16, "AES"); + } + catch(NoSuchAlgorithmException | InvalidKeyException e) { + Log.SYSTEM.error(e, "Konnte Diffie-Hellman-Schlüsselaushandlung nicht absolvieren"); + return null; + } + } + + public static KeyPair createDHKeypair() { + try { + KeyPairGenerator pairgen = KeyPairGenerator.getInstance("X25519"); + return pairgen.generateKeyPair(); } catch(NoSuchAlgorithmException e) { - throw new RuntimeException(e); + Log.SYSTEM.error(e, "Konnte Schlüsselpaar für Diffie-Hellman nicht generieren"); + return null; } } public static KeyPair createKeypair() { try { - KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA"); - pairgen.initialize(4096); + KeyPairGenerator pairgen = KeyPairGenerator.getInstance("Ed25519"); return pairgen.generateKeyPair(); } catch(NoSuchAlgorithmException e) { - Log.SYSTEM.error(e, "Konnte Schlüsselpaar nicht generiren"); + Log.SYSTEM.error(e, "Konnte Schlüsselpaar nicht generieren"); + return null; + } + } + + public static byte[] createSignature(PrivateKey key, byte[] token) { + try { + Signature sig = Signature.getInstance("Ed25519"); + sig.initSign(key); + sig.update(token); + return sig.sign(); + } + catch(SignatureException | InvalidKeyException | NoSuchAlgorithmException e) { + Log.SYSTEM.error(e, "Konnte Signatur nicht generieren"); + return null; + } + } + + public static boolean verifySignature(PublicKey key, byte[] token, byte[] signature) { + try { + Signature sig = Signature.getInstance("Ed25519"); + sig.initVerify(key); + sig.update(token); + return sig.verify(signature); + } + catch(SignatureException | InvalidKeyException | NoSuchAlgorithmException e) { + Log.SYSTEM.error(e, "Konnte Signatur nicht verifizieren"); + return false; + } + } + + public static PublicKey decodeDHPublicKey(byte[] encoded) { + try { + EncodedKeySpec spec = new X509EncodedKeySpec(encoded); + KeyFactory factory = KeyFactory.getInstance("X25519"); + return factory.generatePublic(spec); + } + catch(NoSuchAlgorithmException | InvalidKeySpecException e) { + Log.SYSTEM.error(e, "Öffentlicher Schlüssel für Diffie-Hellman konnte nicht dekodiert werden"); return null; } } @@ -63,7 +130,7 @@ public class EncryptUtil { public static PublicKey decodePublicKey(byte[] encoded) { try { EncodedKeySpec spec = new X509EncodedKeySpec(encoded); - KeyFactory factory = KeyFactory.getInstance("RSA"); + KeyFactory factory = KeyFactory.getInstance("Ed25519"); return factory.generatePublic(spec); } catch(NoSuchAlgorithmException | InvalidKeySpecException e) { @@ -75,7 +142,7 @@ public class EncryptUtil { public static PrivateKey decodePrivateKey(byte[] encoded) { try { EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded); - KeyFactory factory = KeyFactory.getInstance("RSA"); + KeyFactory factory = KeyFactory.getInstance("Ed25519"); return factory.generatePrivate(spec); } catch(NoSuchAlgorithmException | InvalidKeySpecException e) { @@ -84,10 +151,6 @@ public class EncryptUtil { } } - public static SecretKey decryptSharedKey(PrivateKey key, byte[] secret) { - return new SecretKeySpec(decryptData(key, secret), "AES"); - } - public static byte[] encryptData(Key key, byte[] data) { return cipher(Cipher.ENCRYPT_MODE, key, data); } @@ -149,7 +212,7 @@ public class EncryptUtil { } public static String getArmoredPubkey(PublicKey pubkey, String cn) { - StringBuilder sb = new StringBuilder("tcr-rsa-4096 "); + StringBuilder sb = new StringBuilder(EncryptUtil.KEY_ALGO_NAME + " "); sb.append(Base64.getEncoder().encodeToString(pubkey.getEncoded())); sb.append(' ').append(Base64.getEncoder().encodeToString(crc24(pubkey.getEncoded()))); return cn == null || cn.isEmpty() ? sb.toString() : sb.append(' ').append(Util.sanitizeCommonName(cn)).toString(); @@ -159,8 +222,8 @@ public class EncryptUtil { String[] tok = armor.trim().split(" "); if(tok.length != 3 && tok.length != 4) throw new IllegalArgumentException("Key muss aus 3 oder 4 Segmenten bestehen"); - if(!tok[0].equals("tcr-rsa-4096")) - throw new IllegalArgumentException("Algorithmus '" + tok[0] + "' ist nicht unterstützt, es wird derzeit nur tcr-rsa-4096 verwendet"); + if(!tok[0].equals(EncryptUtil.KEY_ALGO_NAME)) + throw new IllegalArgumentException("Algorithmus '" + tok[0] + "' ist nicht unterstützt, es wird derzeit nur " + EncryptUtil.KEY_ALGO_NAME + " verwendet"); byte[] key; try { key = Base64.getDecoder().decode(tok[1]); @@ -183,7 +246,7 @@ public class EncryptUtil { PublicKey pubkey; try { EncodedKeySpec spec = new X509EncodedKeySpec(key); - KeyFactory factory = KeyFactory.getInstance("RSA"); + KeyFactory factory = KeyFactory.getInstance("Ed25519"); pubkey = factory.generatePublic(spec); } catch(NoSuchAlgorithmException | InvalidKeySpecException e) { diff --git a/server/src/main/java/server/Server.java b/server/src/main/java/server/Server.java index ee8e329..2145dda 100755 --- a/server/src/main/java/server/Server.java +++ b/server/src/main/java/server/Server.java @@ -235,7 +235,7 @@ public final class Server implements IThreadListener, Executor { Log.IO.info("Config-Version: %s", version); Log.IO.info("Weltzeit: %d Ticks / %d Sekunden", time, time / 20L); Log.IO.info("Zuletzt geladen: %s", new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date(lastPlayed))); - if(tag.hasByteArray("PrivateKey") && tag.hasByteArray("PublicKey")) { + if(System.getProperty("server.regenkey") == null && tag.hasByteArray("PrivateKey") && tag.hasByteArray("PublicKey")) { PrivateKey key = EncryptUtil.decodePrivateKey(tag.getByteArray("PrivateKey")); PublicKey pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("PublicKey")); if(key != null && pubkey != null) diff --git a/server/src/main/java/server/network/LoginHandler.java b/server/src/main/java/server/network/LoginHandler.java index 8e481ec..10653ff 100755 --- a/server/src/main/java/server/network/LoginHandler.java +++ b/server/src/main/java/server/network/LoginHandler.java @@ -1,13 +1,9 @@ package server.network; +import java.security.KeyPair; import java.security.MessageDigest; -import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; -import java.util.Arrays; - -import javax.crypto.SecretKey; - import common.color.TextColor; import common.log.Log; import common.net.util.concurrent.Future; @@ -39,6 +35,7 @@ public class LoginHandler implements ILoginHandler private final Server server; public final NetConnection netManager; + private final KeyPair tempKeys; private LoginState state = LoginState.INIT; private int timer; @@ -51,6 +48,7 @@ public class LoginHandler implements ILoginHandler { this.netManager = netManager; this.server = server; + this.tempKeys = EncryptUtil.createDHKeypair(); } public void closeConnection(String reason) @@ -139,9 +137,7 @@ public class LoginHandler implements ILoginHandler throw new IllegalStateException("Unerwartetes Handshake-Paket"); if(SVars.encrypt) { this.state = LoginState.ENCRYPT; - this.loginToken = new byte[4]; - TOKEN_RNG.nextBytes(this.loginToken); - this.netManager.sendPacket(new RPacketRequestEncrypt(this.server.getPublicKey(), this.loginToken)); + this.netManager.sendPacket(new RPacketRequestEncrypt(this.server.getPublicKey(), this.tempKeys.getPublic())); } else { this.state = LoginState.PASSWORD; @@ -152,11 +148,7 @@ public class LoginHandler implements ILoginHandler public void processEncryption(LPacketStartEncrypt packet) { if(this.state != LoginState.ENCRYPT) throw new IllegalStateException("Unerwartetes Verschlüsselungs-Paket"); - PrivateKey pkey = this.server.getPrivateKey(); - if(!Arrays.equals(this.loginToken, packet.getToken(pkey))) - throw new IllegalStateException("Fehlerhaftes Token"); - SecretKey key = packet.getKey(pkey); - this.netManager.startEncryption(key); + this.netManager.startEncryption(EncryptUtil.makeKeyAgreement(this.tempKeys.getPrivate(), packet.getKey())); this.state = LoginState.PROOF; } @@ -164,7 +156,7 @@ public class LoginHandler implements ILoginHandler if(this.state != LoginState.PROOF) throw new IllegalStateException("Unerwartetes Anforderungs-Paket"); this.state = LoginState.PASSWORD; - this.netManager.sendPacket(new RPacketResponse(packet.getToken(this.server.getPrivateKey())), new GenericFutureListener < Future > () { + this.netManager.sendPacket(new RPacketResponse(this.server.getPrivateKey(), packet.getToken()), new GenericFutureListener < Future > () { public void operationComplete(Future u) throws Exception { LoginHandler.this.netManager.sendPacket(new RPacketServerConfig(SVars.accessRequired, SVars.authenticate, SVars.authenticate && SVars.passwordAuth, SVars.authenticate && SVars.pubkeyAuth)); @@ -219,7 +211,7 @@ public class LoginHandler implements ILoginHandler this.loginKey = packet.getKey(); this.loginToken = new byte[32]; TOKEN_RNG.nextBytes(this.loginToken); - this.netManager.sendPacket(new RPacketChallenge(this.loginKey, this.loginToken)); + this.netManager.sendPacket(new RPacketChallenge(this.loginToken)); this.state = LoginState.CHALLENGE; } else { @@ -230,7 +222,7 @@ public class LoginHandler implements ILoginHandler public void processResponse(LPacketResponse packet) { if(this.state != LoginState.CHALLENGE) throw new IllegalStateException("Unerwartetes Beweis-Paket"); - if(!Arrays.equals(this.loginToken, packet.getToken())) + if(!packet.verifyToken(this.loginKey, this.loginToken)) throw new IllegalStateException("Fehlerhaftes Beweis-Token"); this.state = LoginState.AUTHENTICATED; }