diff --git a/client/src/client/Client.java b/client/src/client/Client.java index c2b91ed..733137d 100755 --- a/client/src/client/Client.java +++ b/client/src/client/Client.java @@ -137,7 +137,7 @@ import common.packet.CPacketAction; import common.packet.CPacketCheat; import common.packet.CPacketMessage; import common.packet.HPacketHandshake; -import common.packet.LPacketPasswordResponse; +import common.packet.LPacketLogin; import common.packet.CPacketAction.Action; import common.potion.Potion; import common.potion.PotionEffect; @@ -488,9 +488,9 @@ public class Client implements IThreadListener { try { connection = createNetworkManagerAndConnect(address == null ? InetAddress.getLoopbackAddress() : InetAddress.getByName(IDN.toASCII(address)), port); - connection.setNetHandler(new ClientLoginHandler(connection, this)); + connection.setNetHandler(new ClientLoginHandler(connection, this, user, access, pass)); connection.sendPacket(new HPacketHandshake(Config.PROTOCOL)); - connection.sendPacket(new LPacketPasswordResponse(user, access, pass)); + connection.sendPacket(new LPacketLogin()); } catch (UnknownHostException u) { diff --git a/client/src/client/network/ClientLoginHandler.java b/client/src/client/network/ClientLoginHandler.java index c17ab44..cfda5c6 100755 --- a/client/src/client/network/ClientLoginHandler.java +++ b/client/src/client/network/ClientLoginHandler.java @@ -1,21 +1,37 @@ package client.network; +import java.security.PublicKey; + +import javax.crypto.SecretKey; + import client.Client; import common.network.IClientLoginHandler; import common.network.NetConnection; import common.network.NetHandler; import common.network.PacketRegistry; +import common.packet.LPacketPasswordResponse; +import common.packet.LPacketStartEncrypt; import common.packet.RPacketDisconnect; import common.packet.RPacketEnableCompression; import common.packet.RPacketLoginSuccess; +import common.packet.RPacketRequestEncrypt; +import common.util.EncryptUtil; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; public class ClientLoginHandler extends NetHandler implements IClientLoginHandler { private final Client gm; private final NetConnection networkManager; + private final String user; + private final String access; + private final String password; - public ClientLoginHandler(NetConnection conn, Client gmIn) { + public ClientLoginHandler(NetConnection conn, Client gmIn, String userIn, String accessIn, String passwordIn) { this.networkManager = conn; this.gm = gmIn; + this.user = userIn; + this.access = accessIn; + this.password = passwordIn; } public void onDisconnect(String reason) @@ -23,10 +39,21 @@ public class ClientLoginHandler extends NetHandler implements IClientLoginHandle this.gm.disconnected(reason); } - public final void handleDisconnect(RPacketDisconnect packetIn) + public void handleDisconnect(RPacketDisconnect packetIn) { this.networkManager.closeChannel(packetIn.getReason()); } + + public void handleEncrypt(RPacketRequestEncrypt packet) { + final SecretKey secret = EncryptUtil.createNewSharedKey(); + PublicKey pubkey = packet.getKey(); + this.networkManager.sendPacket(new LPacketStartEncrypt(secret, pubkey, packet.getToken()), new GenericFutureListener < Future > () { + public void operationComplete(Future u) throws Exception { + ClientLoginHandler.this.networkManager.startEncryption(secret); + ClientLoginHandler.this.networkManager.sendPacket(new LPacketPasswordResponse(ClientLoginHandler.this.user, ClientLoginHandler.this.access, ClientLoginHandler.this.password)); + } + }); + } public void handleLoginSuccess(RPacketLoginSuccess packetIn) { @@ -35,24 +62,8 @@ public class ClientLoginHandler extends NetHandler implements IClientLoginHandle this.networkManager.setNetHandler(new ClientPlayer(this.gm, this.networkManager)); } - public final void handleEnableCompression(RPacketEnableCompression packetIn) + public void handleEnableCompression(RPacketEnableCompression packetIn) { this.networkManager.setCompressionTreshold(packetIn.getValue()); } - -// public void handlePasswordRequest(RPacketPasswordRequest packetIn) { -// if(this.server == null) { -// this.networkManager.sendPacket(new LPacketPasswordResponse(this.user, "", "")); -// } -// else if((packetIn.getPasswordRequested() && this.server.pass.isEmpty()) || -// (packetIn.getPasswordProtected() && this.server.access.isEmpty())) { -//// this.toChange = this.gm.getConnected(); -// this.accessRequired = packetIn.getPasswordProtected() && this.server.access.isEmpty(); -// this.passwordRequired = packetIn.getPasswordRequested() && this.server.pass.isEmpty(); -// this.networkManager.closeChannel(""); -// } -// else { -// this.networkManager.sendPacket(new LPacketPasswordResponse(this.user, this.access, this.pass)); -// } -// } } diff --git a/common/src/common/init/Config.java b/common/src/common/init/Config.java index 652f2f7..d04c4e4 100755 --- a/common/src/common/init/Config.java +++ b/common/src/common/init/Config.java @@ -333,8 +333,6 @@ public abstract class Config { public static boolean itemFallDamage = true; @Var(name = "registration") public static boolean register = true; - @Var(name = "authentication") - public static boolean auth = true; @Var(name = "signEditing") public static boolean editSigns = true; @@ -350,21 +348,6 @@ public abstract class Config { public static boolean rabidRabbits = false; @Var(name = "snowStacking") public static boolean snowStack = false; -// @Var(name = "teleportForAll") -// public static boolean teleportAllowed = false; -// @Var(name = "preload_chunks_all") // Vorsicht Lag!! -// public static boolean preloadAll = false; - -// @Var(name = "maxPolygonalPoints") -// public static int polygonalPoints = -1; -// @Var(name = "maxPolyhedronPoints") -// public static int polyhedronPoints = -1; -// @Var(name = "maxEditRadius") -// public static int editRadius = -1; -// @Var(name = "maxBrushRadius") -// public static int brushRadius = 500; -// @Var(name = "historySize") -// public static int history = 300; @Var(name = "randomTickSpeed") public static int randomTick = 3; diff --git a/common/src/common/network/EncryptionCodec.java b/common/src/common/network/EncryptionCodec.java new file mode 100644 index 0000000..2598ecc --- /dev/null +++ b/common/src/common/network/EncryptionCodec.java @@ -0,0 +1,41 @@ +package common.network; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import javax.crypto.Cipher; +import javax.crypto.ShortBufferException; + +public class EncryptionCodec { + private final Cipher cipher; + private byte[] receiveBuf = new byte[0]; + private byte[] encodeBuf = new byte[0]; + + protected EncryptionCodec(Cipher cipher) { + this.cipher = cipher; + } + + private byte[] readBuffer(ByteBuf buf) { + int size = buf.readableBytes(); + if(this.receiveBuf.length < size) + this.receiveBuf = new byte[size]; + buf.readBytes((byte[])this.receiveBuf, 0, size); + return this.receiveBuf; + } + + protected ByteBuf decipher(ChannelHandlerContext ctx, ByteBuf buffer) throws ShortBufferException { + int size = buffer.readableBytes(); + byte[] data = this.readBuffer(buffer); + ByteBuf buf = ctx.alloc().heapBuffer(this.cipher.getOutputSize(size)); + buf.writerIndex(this.cipher.update(data, 0, size, buf.array(), buf.arrayOffset())); + return buf; + } + + protected void cipher(ByteBuf in, ByteBuf out) throws ShortBufferException { + int size = in.readableBytes(); + byte[] data = this.readBuffer(in); + int csize = this.cipher.getOutputSize(size); + if(this.encodeBuf.length < csize) + this.encodeBuf = new byte[csize]; + out.writeBytes((byte[])this.encodeBuf, 0, this.cipher.update(data, 0, size, this.encodeBuf)); + } +} diff --git a/common/src/common/network/IClientLoginHandler.java b/common/src/common/network/IClientLoginHandler.java index 99ac69d..b2606e1 100644 --- a/common/src/common/network/IClientLoginHandler.java +++ b/common/src/common/network/IClientLoginHandler.java @@ -3,6 +3,7 @@ package common.network; import common.packet.RPacketDisconnect; import common.packet.RPacketEnableCompression; import common.packet.RPacketLoginSuccess; +import common.packet.RPacketRequestEncrypt; public interface IClientLoginHandler { @@ -12,4 +13,6 @@ public interface IClientLoginHandler { void handleEnableCompression(RPacketEnableCompression packetIn); + void handleEncrypt(RPacketRequestEncrypt packet); + } \ No newline at end of file diff --git a/common/src/common/network/ILoginHandler.java b/common/src/common/network/ILoginHandler.java index 5361bc1..479e986 100644 --- a/common/src/common/network/ILoginHandler.java +++ b/common/src/common/network/ILoginHandler.java @@ -1,9 +1,15 @@ package common.network; +import common.packet.LPacketLogin; import common.packet.LPacketPasswordResponse; +import common.packet.LPacketStartEncrypt; public interface ILoginHandler { void processPasswordResponse(LPacketPasswordResponse packetIn); + void processEncryption(LPacketStartEncrypt packet); + + void processLogin(LPacketLogin packet); + } \ No newline at end of file diff --git a/common/src/common/network/NetConnection.java b/common/src/common/network/NetConnection.java index a77835e..5a0530a 100755 --- a/common/src/common/network/NetConnection.java +++ b/common/src/common/network/NetConnection.java @@ -1,13 +1,18 @@ package common.network; import java.net.SocketAddress; +import java.nio.channels.ClosedChannelException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + import common.log.Log; import common.network.NetHandler.ThreadQuickExitException; +import common.util.EncryptUtil; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -73,7 +78,8 @@ public class NetConnection extends SimpleChannelInboundHandler else { comp = "Interner Fehler: " + p_exceptionCaught_2_; - Log.SYSTEM.error(p_exceptionCaught_2_, "Fehler in der Verbindung mit %s", this.getCutAddress()); + if(!(p_exceptionCaught_2_ instanceof ClosedChannelException)) + Log.SYSTEM.error(p_exceptionCaught_2_, "Fehler in der Verbindung mit %s", this.getCutAddress()); } this.closeChannel(comp); @@ -356,6 +362,11 @@ public class NetConnection extends SimpleChannelInboundHandler } } } + + public void startEncryption(SecretKey key) { + this.channel.pipeline().addBefore("splitter", "decrypt", new NettyEncryptionDecoder(EncryptUtil.createNetCipherInstance(Cipher.DECRYPT_MODE, key))); + this.channel.pipeline().addBefore("prepender", "encrypt", new NettyEncryptionEncoder(EncryptUtil.createNetCipherInstance(Cipher.ENCRYPT_MODE, key))); + } static class InboundHandlerTuplePacketListener { diff --git a/common/src/common/network/NettyEncryptionDecoder.java b/common/src/common/network/NettyEncryptionDecoder.java new file mode 100644 index 0000000..dac5156 --- /dev/null +++ b/common/src/common/network/NettyEncryptionDecoder.java @@ -0,0 +1,20 @@ +package common.network; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import java.util.List; +import javax.crypto.Cipher; +import javax.crypto.ShortBufferException; + +public class NettyEncryptionDecoder extends MessageToMessageDecoder { + private final EncryptionCodec codec; + + public NettyEncryptionDecoder(Cipher cipher) { + this.codec = new EncryptionCodec(cipher); + } + + protected void decode(ChannelHandlerContext p_decode_1_, ByteBuf p_decode_2_, List p_decode_3_) throws ShortBufferException, Exception { + p_decode_3_.add(this.codec.decipher(p_decode_1_, p_decode_2_)); + } +} diff --git a/common/src/common/network/NettyEncryptionEncoder.java b/common/src/common/network/NettyEncryptionEncoder.java new file mode 100644 index 0000000..0ec56f2 --- /dev/null +++ b/common/src/common/network/NettyEncryptionEncoder.java @@ -0,0 +1,19 @@ +package common.network; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import javax.crypto.Cipher; +import javax.crypto.ShortBufferException; + +public class NettyEncryptionEncoder extends MessageToByteEncoder { + private final EncryptionCodec codec; + + public NettyEncryptionEncoder(Cipher cipher) { + this.codec = new EncryptionCodec(cipher); + } + + protected void encode(ChannelHandlerContext p_encode_1_, ByteBuf p_encode_2_, ByteBuf p_encode_3_) throws ShortBufferException, Exception { + this.codec.cipher(p_encode_2_, p_encode_3_); + } +} diff --git a/common/src/common/network/PacketRegistry.java b/common/src/common/network/PacketRegistry.java index 0a13153..fd8e7c0 100755 --- a/common/src/common/network/PacketRegistry.java +++ b/common/src/common/network/PacketRegistry.java @@ -20,10 +20,13 @@ import common.packet.CPacketPlayer; import common.packet.CPacketSign; import common.packet.CPacketSkin; import common.packet.HPacketHandshake; +import common.packet.LPacketLogin; import common.packet.LPacketPasswordResponse; +import common.packet.LPacketStartEncrypt; import common.packet.RPacketDisconnect; import common.packet.RPacketEnableCompression; import common.packet.RPacketLoginSuccess; +import common.packet.RPacketRequestEncrypt; import common.packet.S14PacketEntity; import common.packet.S18PacketEntityTeleport; import common.packet.S19PacketEntityHeadLook; @@ -99,10 +102,11 @@ public enum PacketRegistry { { this.server(RPacketDisconnect.class); + this.server(RPacketRequestEncrypt.class); this.server(RPacketLoginSuccess.class); this.server(RPacketEnableCompression.class); -// this.server(RPacketPasswordRequest.class); -// this.client(LPacketLoginStart.class); + this.client(LPacketLogin.class); + this.client(LPacketStartEncrypt.class); this.client(LPacketPasswordResponse.class); } }, diff --git a/common/src/common/packet/LPacketLogin.java b/common/src/common/packet/LPacketLogin.java new file mode 100644 index 0000000..671fca7 --- /dev/null +++ b/common/src/common/packet/LPacketLogin.java @@ -0,0 +1,21 @@ +package common.packet; + +import java.io.IOException; +import common.network.ILoginHandler; +import common.network.Packet; +import common.network.PacketBuffer; + +public class LPacketLogin implements Packet { + public LPacketLogin() { + } + + public void readPacketData(PacketBuffer buf) throws IOException { + } + + public void writePacketData(PacketBuffer buf) throws IOException { + } + + public void processPacket(ILoginHandler handler) { + handler.processLogin(this); + } +} diff --git a/common/src/common/packet/LPacketStartEncrypt.java b/common/src/common/packet/LPacketStartEncrypt.java new file mode 100644 index 0000000..3408450 --- /dev/null +++ b/common/src/common/packet/LPacketStartEncrypt.java @@ -0,0 +1,47 @@ +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]; + + public LPacketStartEncrypt() { + } + + public LPacketStartEncrypt(SecretKey secret, PublicKey pubkey, byte[] token) { + this.key = EncryptUtil.encryptData(pubkey, secret.getEncoded()); + this.token = EncryptUtil.encryptData(pubkey, token); + } + + public void readPacketData(PacketBuffer buf) throws IOException { + this.key = buf.readByteArray(); + this.token = buf.readByteArray(); + } + + public void writePacketData(PacketBuffer buf) throws IOException { + buf.writeByteArray(this.key); + buf.writeByteArray(this.token); + } + + 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 key == null ? this.token : EncryptUtil.decryptData(key, this.token); + } +} diff --git a/common/src/common/packet/RPacketRequestEncrypt.java b/common/src/common/packet/RPacketRequestEncrypt.java new file mode 100644 index 0000000..2b45964 --- /dev/null +++ b/common/src/common/packet/RPacketRequestEncrypt.java @@ -0,0 +1,44 @@ +package common.packet; + +import java.io.IOException; +import java.security.PublicKey; + +import common.network.IClientLoginHandler; +import common.network.Packet; +import common.network.PacketBuffer; +import common.util.EncryptUtil; + +public class RPacketRequestEncrypt implements Packet { + private PublicKey key; + private byte[] token; + + public RPacketRequestEncrypt() { + } + + public RPacketRequestEncrypt(PublicKey key, byte[] token) { + this.key = key; + this.token = token; + } + + public final void readPacketData(PacketBuffer buf) throws IOException { + this.key = EncryptUtil.decodePublicKey(buf.readByteArray()); + this.token = buf.readByteArray(); + } + + public final void writePacketData(PacketBuffer buf) throws IOException { + buf.writeByteArray(this.key.getEncoded()); + buf.writeByteArray(this.token); + } + + public void processPacket(IClientLoginHandler handler) { + handler.handleEncrypt(this); + } + + public PublicKey getKey() { + return this.key; + } + + public byte[] getToken() { + return this.token; + } +} diff --git a/common/src/common/util/EncryptUtil.java b/common/src/common/util/EncryptUtil.java new file mode 100644 index 0000000..3ef5080 --- /dev/null +++ b/common/src/common/util/EncryptUtil.java @@ -0,0 +1,135 @@ +package common.util; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import common.log.Log; + +public class EncryptUtil { + public static SecretKey createNewSharedKey() { + try { + KeyGenerator keygen = KeyGenerator.getInstance("AES"); + keygen.init(128); + return keygen.generateKey(); + } + catch(NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public static KeyPair generateKeyPair() { + try { + KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA"); + pairgen.initialize(1024); + return pairgen.generateKeyPair(); + } + catch(NoSuchAlgorithmException e) { + Log.SYSTEM.error(e, "Konnte Schlüsselpaar nicht generiren"); + return null; + } + } + + public static byte[] getServerIdHash(String id, PublicKey pubkey, SecretKey secret) { + try { + return digestOperation("SHA-1", new byte[][] {id.getBytes("ISO_8859_1"), secret.getEncoded(), pubkey.getEncoded()}); + } + catch(UnsupportedEncodingException e) { + Log.SYSTEM.error(e, "Konnte Server-Prüfwert nicht berechnen"); + return null; + } + } + + private static byte[] digestOperation(String algorithm, byte[]... data) { + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + + for(byte[] abyte : data) { + digest.update(abyte); + } + + return digest.digest(); + } + catch(NoSuchAlgorithmException e) { + Log.SYSTEM.error(e, "Konnte Prüfwert nicht berechnen"); + return null; + } + } + + public static PublicKey decodePublicKey(byte[] encoded) { + try { + EncodedKeySpec spec = new X509EncodedKeySpec(encoded); + KeyFactory factory = KeyFactory.getInstance("RSA"); + return factory.generatePublic(spec); + } + catch(NoSuchAlgorithmException | InvalidKeySpecException e) { + Log.SYSTEM.error(e, "Öffentlicher Schlüssel konnte nicht dekodiert werden"); + return null; + } + } + + public static SecretKey decryptSharedKey(PrivateKey key, byte[] secret) { + return new SecretKeySpec(decryptData(key, secret), "AES"); + } + + public static byte[] encryptData(Key key, byte[] data) { + return cipherOperation(Cipher.ENCRYPT_MODE, key, data); + } + + public static byte[] decryptData(Key key, byte[] data) { + return cipherOperation(Cipher.DECRYPT_MODE, key, data); + } + + private static byte[] cipherOperation(int mode, Key key, byte[] data) { + try { + return createCipher(mode, key.getAlgorithm(), key).doFinal(data); + } + catch(IllegalBlockSizeException | BadPaddingException e) { + Log.SYSTEM.error(e, "Konnte Daten nicht ver- oder entschlüsseln"); + return null; + } + } + + private static Cipher createCipher(int mode, String transformation, Key key) { + try { + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(mode, key); + return cipher; + } + catch(InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + Log.SYSTEM.error(e, "Konnte Verschlüsselungsverfahren nicht initialisieren"); + return null; + } + } + + public static Cipher createNetCipherInstance(int mode, Key key) { + try { + Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding"); + cipher.init(mode, (Key)key, (AlgorithmParameterSpec)(new IvParameterSpec(key.getEncoded()))); + return cipher; + } + catch(GeneralSecurityException e) { + throw new RuntimeException(e); + } + } +} diff --git a/server/src/server/Server.java b/server/src/server/Server.java index 8b13220..a4bac0a 100755 --- a/server/src/server/Server.java +++ b/server/src/server/Server.java @@ -6,6 +6,9 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; @@ -67,6 +70,7 @@ import common.packet.SPacketTimeUpdate; import common.packet.SPacketWorld; import common.potion.PotionEffect; import common.util.BlockPos; +import common.util.EncryptUtil; import common.util.ExtMath; import common.util.LazyLoadBase; import common.util.PortalType; @@ -118,6 +122,7 @@ public final class Server implements IThreadListener { private final List unload = Lists.newArrayList(); private final Map warps = Maps.newTreeMap(); private final CommandEnvironment scriptEnv = new CommandEnvironment(this); + private final KeyPair keyPair; private final boolean debug; private WorldServer space; @@ -238,6 +243,7 @@ public final class Server implements IThreadListener { } } }, "viewDistance"); + this.keyPair = EncryptUtil.generateKeyPair(); } public CommandEnvironment getScriptEnvironment() { @@ -811,24 +817,22 @@ public final class Server implements IThreadListener { conn.readFromNBT(tag); if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.isAdmin()) return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit); - if(/* !connection.isLocalChannel() && */ Config.auth) { - if(conn.getPassword() == null) { - if(!Config.register) - return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; - if(loginPass.length() == 0) - return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)"; - if(loginPass.length() < Config.minPassLength) - return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen"; - conn.setPassword(loginPass); - Log.JNI.info(loginUser + " registrierte sich mit Passwort"); - } - else if(!conn.getPassword().equals(loginPass)) { - return "Falsches Passwort"; - } - else { - Log.JNI.info(loginUser + " loggte sich mit Passwort ein"); - } - } + if(conn.getPassword() == null) { + if(!Config.register) + return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; + if(loginPass.length() == 0) + return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)"; + if(loginPass.length() < Config.minPassLength) + return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen"; + conn.setPassword(loginPass); + Log.JNI.info(loginUser + " registrierte sich mit Passwort"); + } + else if(!conn.getPassword().equals(loginPass)) { + return "Falsches Passwort"; + } + else { + Log.JNI.info(loginUser + " loggte sich mit Passwort ein"); + } if(Config.compression >= 0) { connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { @@ -1300,4 +1304,12 @@ public final class Server implements IThreadListener { public void logConsole(String message) { Log.CONSOLE.info(message); } + + public PublicKey getPublicKey() { + return this.keyPair.getPublic(); + } + + public PrivateKey getPrivateKey() { + return this.keyPair.getPrivate(); + } } diff --git a/server/src/server/network/LoginHandler.java b/server/src/server/network/LoginHandler.java index a215180..d71d102 100755 --- a/server/src/server/network/LoginHandler.java +++ b/server/src/server/network/LoginHandler.java @@ -1,5 +1,11 @@ package server.network; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.util.Arrays; + +import javax.crypto.SecretKey; + import common.color.TextColor; import common.init.Config; import common.log.Log; @@ -7,23 +13,29 @@ import common.network.ILoginHandler; import common.network.IPlayer; import common.network.NetConnection; import common.network.NetHandler; +import common.packet.LPacketLogin; import common.packet.LPacketPasswordResponse; +import common.packet.LPacketStartEncrypt; import common.packet.RPacketDisconnect; +import common.packet.RPacketRequestEncrypt; import server.Server; public class LoginHandler extends NetHandler implements ILoginHandler { private static enum LoginState { - PASSWORD, READY_TO_ACCEPT, ACCEPTED; + INIT, ENCRYPT, PASSWORD, AUTHENTICATED, ACCEPTED; } + private static final SecureRandom TOKEN_RNG = new SecureRandom(); + private final Server server; public final NetConnection netManager; - private LoginState state = LoginState.PASSWORD; + private LoginState state = LoginState.INIT; private int timer; private String loginUser; private String loginPass; + private byte[] loginToken; public LoginHandler(Server server, NetConnection netManager) { @@ -58,7 +70,7 @@ public class LoginHandler extends NetHandler implements ILoginHandler public void update() { - if(this.state == LoginState.READY_TO_ACCEPT) + if(this.state == LoginState.AUTHENTICATED) this.tryAcceptPlayer(); // else if (this.currentLoginState == LoginState.DELAY_ACCEPT) // { @@ -93,15 +105,25 @@ public class LoginHandler extends NetHandler implements ILoginHandler this.state = LoginState.ACCEPTED; } -// public void processLoginStart(LPacketLoginStart packetIn) -// { -// if(this.state != LoginState.HELLO) -// throw new IllegalStateException("Unerwartetes Start-Paket"); -// if(!this.netManager.isLocalChannel()) { -// this.state = LoginState.PASSWORD; -// this.netManager.sendPacket(new RPacketPasswordRequest()); // !Config.password.isEmpty(), Config.auth -// } -// } + public void processLogin(LPacketLogin packet) { + if(this.state != LoginState.INIT) + throw new IllegalStateException("Unerwartetes Login-Paket"); + this.state = LoginState.ENCRYPT; + this.loginToken = new byte[4]; + TOKEN_RNG.nextBytes(this.loginToken); + this.netManager.sendPacket(new RPacketRequestEncrypt(this.server.getPublicKey(), this.loginToken)); + } + + 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.state = LoginState.PASSWORD; + } public void processPasswordResponse(LPacketPasswordResponse packetIn) { if(this.state != LoginState.PASSWORD) @@ -116,6 +138,6 @@ public class LoginHandler extends NetHandler implements ILoginHandler this.closeConnection("Falsches Zugangspasswort"); return; } - this.state = LoginState.READY_TO_ACCEPT; + this.state = LoginState.AUTHENTICATED; } }