add encryption and enforce authentication

This commit is contained in:
Sen 2025-05-18 17:39:22 +02:00
parent 2ea3267e3a
commit 8d4b4b3619
16 changed files with 452 additions and 73 deletions

View file

@ -137,7 +137,7 @@ import common.packet.CPacketAction;
import common.packet.CPacketCheat; import common.packet.CPacketCheat;
import common.packet.CPacketMessage; import common.packet.CPacketMessage;
import common.packet.HPacketHandshake; import common.packet.HPacketHandshake;
import common.packet.LPacketPasswordResponse; import common.packet.LPacketLogin;
import common.packet.CPacketAction.Action; import common.packet.CPacketAction.Action;
import common.potion.Potion; import common.potion.Potion;
import common.potion.PotionEffect; import common.potion.PotionEffect;
@ -488,9 +488,9 @@ public class Client implements IThreadListener {
try try
{ {
connection = createNetworkManagerAndConnect(address == null ? InetAddress.getLoopbackAddress() : InetAddress.getByName(IDN.toASCII(address)), port); 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 HPacketHandshake(Config.PROTOCOL));
connection.sendPacket(new LPacketPasswordResponse(user, access, pass)); connection.sendPacket(new LPacketLogin());
} }
catch (UnknownHostException u) catch (UnknownHostException u)
{ {

View file

@ -1,21 +1,37 @@
package client.network; package client.network;
import java.security.PublicKey;
import javax.crypto.SecretKey;
import client.Client; import client.Client;
import common.network.IClientLoginHandler; import common.network.IClientLoginHandler;
import common.network.NetConnection; import common.network.NetConnection;
import common.network.NetHandler; import common.network.NetHandler;
import common.network.PacketRegistry; import common.network.PacketRegistry;
import common.packet.LPacketPasswordResponse;
import common.packet.LPacketStartEncrypt;
import common.packet.RPacketDisconnect; import common.packet.RPacketDisconnect;
import common.packet.RPacketEnableCompression; import common.packet.RPacketEnableCompression;
import common.packet.RPacketLoginSuccess; 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 { public class ClientLoginHandler extends NetHandler implements IClientLoginHandler {
private final Client gm; private final Client gm;
private final NetConnection networkManager; 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.networkManager = conn;
this.gm = gmIn; this.gm = gmIn;
this.user = userIn;
this.access = accessIn;
this.password = passwordIn;
} }
public void onDisconnect(String reason) public void onDisconnect(String reason)
@ -23,11 +39,22 @@ public class ClientLoginHandler extends NetHandler implements IClientLoginHandle
this.gm.disconnected(reason); this.gm.disconnected(reason);
} }
public final void handleDisconnect(RPacketDisconnect packetIn) public void handleDisconnect(RPacketDisconnect packetIn)
{ {
this.networkManager.closeChannel(packetIn.getReason()); 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 <? super Void >> () {
public void operationComplete(Future <? super Void > 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) public void handleLoginSuccess(RPacketLoginSuccess packetIn)
{ {
this.gm.debugWorld = packetIn.isDebug(); this.gm.debugWorld = packetIn.isDebug();
@ -35,24 +62,8 @@ public class ClientLoginHandler extends NetHandler implements IClientLoginHandle
this.networkManager.setNetHandler(new ClientPlayer(this.gm, this.networkManager)); 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()); 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));
// }
// }
} }

View file

@ -333,8 +333,6 @@ public abstract class Config {
public static boolean itemFallDamage = true; public static boolean itemFallDamage = true;
@Var(name = "registration") @Var(name = "registration")
public static boolean register = true; public static boolean register = true;
@Var(name = "authentication")
public static boolean auth = true;
@Var(name = "signEditing") @Var(name = "signEditing")
public static boolean editSigns = true; public static boolean editSigns = true;
@ -350,21 +348,6 @@ public abstract class Config {
public static boolean rabidRabbits = false; public static boolean rabidRabbits = false;
@Var(name = "snowStacking") @Var(name = "snowStacking")
public static boolean snowStack = false; 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") @Var(name = "randomTickSpeed")
public static int randomTick = 3; public static int randomTick = 3;

View file

@ -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));
}
}

View file

@ -3,6 +3,7 @@ package common.network;
import common.packet.RPacketDisconnect; import common.packet.RPacketDisconnect;
import common.packet.RPacketEnableCompression; import common.packet.RPacketEnableCompression;
import common.packet.RPacketLoginSuccess; import common.packet.RPacketLoginSuccess;
import common.packet.RPacketRequestEncrypt;
public interface IClientLoginHandler { public interface IClientLoginHandler {
@ -12,4 +13,6 @@ public interface IClientLoginHandler {
void handleEnableCompression(RPacketEnableCompression packetIn); void handleEnableCompression(RPacketEnableCompression packetIn);
void handleEncrypt(RPacketRequestEncrypt packet);
} }

View file

@ -1,9 +1,15 @@
package common.network; package common.network;
import common.packet.LPacketLogin;
import common.packet.LPacketPasswordResponse; import common.packet.LPacketPasswordResponse;
import common.packet.LPacketStartEncrypt;
public interface ILoginHandler { public interface ILoginHandler {
void processPasswordResponse(LPacketPasswordResponse packetIn); void processPasswordResponse(LPacketPasswordResponse packetIn);
void processEncryption(LPacketStartEncrypt packet);
void processLogin(LPacketLogin packet);
} }

View file

@ -1,13 +1,18 @@
package common.network; package common.network;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.channels.ClosedChannelException;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import common.log.Log; import common.log.Log;
import common.network.NetHandler.ThreadQuickExitException; import common.network.NetHandler.ThreadQuickExitException;
import common.util.EncryptUtil;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
@ -73,7 +78,8 @@ public class NetConnection extends SimpleChannelInboundHandler<Packet>
else else
{ {
comp = "Interner Fehler: " + p_exceptionCaught_2_; 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); this.closeChannel(comp);
@ -357,6 +363,11 @@ public class NetConnection extends SimpleChannelInboundHandler<Packet>
} }
} }
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 static class InboundHandlerTuplePacketListener
{ {
private final Packet packet; private final Packet packet;

View file

@ -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<ByteBuf> {
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<Object> p_decode_3_) throws ShortBufferException, Exception {
p_decode_3_.add(this.codec.decipher(p_decode_1_, p_decode_2_));
}
}

View file

@ -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<ByteBuf> {
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_);
}
}

View file

@ -20,10 +20,13 @@ import common.packet.CPacketPlayer;
import common.packet.CPacketSign; import common.packet.CPacketSign;
import common.packet.CPacketSkin; import common.packet.CPacketSkin;
import common.packet.HPacketHandshake; import common.packet.HPacketHandshake;
import common.packet.LPacketLogin;
import common.packet.LPacketPasswordResponse; import common.packet.LPacketPasswordResponse;
import common.packet.LPacketStartEncrypt;
import common.packet.RPacketDisconnect; import common.packet.RPacketDisconnect;
import common.packet.RPacketEnableCompression; import common.packet.RPacketEnableCompression;
import common.packet.RPacketLoginSuccess; import common.packet.RPacketLoginSuccess;
import common.packet.RPacketRequestEncrypt;
import common.packet.S14PacketEntity; import common.packet.S14PacketEntity;
import common.packet.S18PacketEntityTeleport; import common.packet.S18PacketEntityTeleport;
import common.packet.S19PacketEntityHeadLook; import common.packet.S19PacketEntityHeadLook;
@ -99,10 +102,11 @@ public enum PacketRegistry
{ {
{ {
this.server(RPacketDisconnect.class); this.server(RPacketDisconnect.class);
this.server(RPacketRequestEncrypt.class);
this.server(RPacketLoginSuccess.class); this.server(RPacketLoginSuccess.class);
this.server(RPacketEnableCompression.class); this.server(RPacketEnableCompression.class);
// this.server(RPacketPasswordRequest.class); this.client(LPacketLogin.class);
// this.client(LPacketLoginStart.class); this.client(LPacketStartEncrypt.class);
this.client(LPacketPasswordResponse.class); this.client(LPacketPasswordResponse.class);
} }
}, },

View file

@ -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<ILoginHandler> {
public LPacketLogin() {
}
public void readPacketData(PacketBuffer buf) throws IOException {
}
public void writePacketData(PacketBuffer buf) throws IOException {
}
public void processPacket(ILoginHandler handler) {
handler.processLogin(this);
}
}

View file

@ -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<ILoginHandler> {
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);
}
}

View file

@ -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<IClientLoginHandler> {
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;
}
}

View file

@ -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);
}
}
}

View file

@ -6,6 +6,9 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.InetAddress; import java.net.InetAddress;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
@ -67,6 +70,7 @@ import common.packet.SPacketTimeUpdate;
import common.packet.SPacketWorld; import common.packet.SPacketWorld;
import common.potion.PotionEffect; import common.potion.PotionEffect;
import common.util.BlockPos; import common.util.BlockPos;
import common.util.EncryptUtil;
import common.util.ExtMath; import common.util.ExtMath;
import common.util.LazyLoadBase; import common.util.LazyLoadBase;
import common.util.PortalType; import common.util.PortalType;
@ -118,6 +122,7 @@ public final class Server implements IThreadListener {
private final List<Dimension> unload = Lists.<Dimension>newArrayList(); private final List<Dimension> unload = Lists.<Dimension>newArrayList();
private final Map<String, Position> warps = Maps.<String, Position>newTreeMap(); private final Map<String, Position> warps = Maps.<String, Position>newTreeMap();
private final CommandEnvironment scriptEnv = new CommandEnvironment(this); private final CommandEnvironment scriptEnv = new CommandEnvironment(this);
private final KeyPair keyPair;
private final boolean debug; private final boolean debug;
private WorldServer space; private WorldServer space;
@ -238,6 +243,7 @@ public final class Server implements IThreadListener {
} }
} }
}, "viewDistance"); }, "viewDistance");
this.keyPair = EncryptUtil.generateKeyPair();
} }
public CommandEnvironment getScriptEnvironment() { public CommandEnvironment getScriptEnvironment() {
@ -811,24 +817,22 @@ public final class Server implements IThreadListener {
conn.readFromNBT(tag); conn.readFromNBT(tag);
if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.isAdmin()) 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); return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit);
if(/* !connection.isLocalChannel() && */ Config.auth) { if(conn.getPassword() == null) {
if(conn.getPassword() == null) { if(!Config.register)
if(!Config.register) return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)";
return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; if(loginPass.length() == 0)
if(loginPass.length() == 0) return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)";
return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)"; if(loginPass.length() < Config.minPassLength)
if(loginPass.length() < Config.minPassLength) return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen";
return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen"; conn.setPassword(loginPass);
conn.setPassword(loginPass); Log.JNI.info(loginUser + " registrierte sich mit Passwort");
Log.JNI.info(loginUser + " registrierte sich mit Passwort"); }
} else if(!conn.getPassword().equals(loginPass)) {
else if(!conn.getPassword().equals(loginPass)) { return "Falsches Passwort";
return "Falsches Passwort"; }
} else {
else { Log.JNI.info(loginUser + " loggte sich mit Passwort ein");
Log.JNI.info(loginUser + " loggte sich mit Passwort ein"); }
}
}
if(Config.compression >= 0) { if(Config.compression >= 0) {
connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() { connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception { public void operationComplete(ChannelFuture future) throws Exception {
@ -1300,4 +1304,12 @@ public final class Server implements IThreadListener {
public void logConsole(String message) { public void logConsole(String message) {
Log.CONSOLE.info(message); Log.CONSOLE.info(message);
} }
public PublicKey getPublicKey() {
return this.keyPair.getPublic();
}
public PrivateKey getPrivateKey() {
return this.keyPair.getPrivate();
}
} }

View file

@ -1,5 +1,11 @@
package server.network; 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.color.TextColor;
import common.init.Config; import common.init.Config;
import common.log.Log; import common.log.Log;
@ -7,23 +13,29 @@ import common.network.ILoginHandler;
import common.network.IPlayer; import common.network.IPlayer;
import common.network.NetConnection; import common.network.NetConnection;
import common.network.NetHandler; import common.network.NetHandler;
import common.packet.LPacketLogin;
import common.packet.LPacketPasswordResponse; import common.packet.LPacketPasswordResponse;
import common.packet.LPacketStartEncrypt;
import common.packet.RPacketDisconnect; import common.packet.RPacketDisconnect;
import common.packet.RPacketRequestEncrypt;
import server.Server; import server.Server;
public class LoginHandler extends NetHandler implements ILoginHandler public class LoginHandler extends NetHandler implements ILoginHandler
{ {
private static enum LoginState { 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; private final Server server;
public final NetConnection netManager; public final NetConnection netManager;
private LoginState state = LoginState.PASSWORD; private LoginState state = LoginState.INIT;
private int timer; private int timer;
private String loginUser; private String loginUser;
private String loginPass; private String loginPass;
private byte[] loginToken;
public LoginHandler(Server server, NetConnection netManager) public LoginHandler(Server server, NetConnection netManager)
{ {
@ -58,7 +70,7 @@ public class LoginHandler extends NetHandler implements ILoginHandler
public void update() public void update()
{ {
if(this.state == LoginState.READY_TO_ACCEPT) if(this.state == LoginState.AUTHENTICATED)
this.tryAcceptPlayer(); this.tryAcceptPlayer();
// else if (this.currentLoginState == LoginState.DELAY_ACCEPT) // else if (this.currentLoginState == LoginState.DELAY_ACCEPT)
// { // {
@ -93,15 +105,25 @@ public class LoginHandler extends NetHandler implements ILoginHandler
this.state = LoginState.ACCEPTED; this.state = LoginState.ACCEPTED;
} }
// public void processLoginStart(LPacketLoginStart packetIn) public void processLogin(LPacketLogin packet) {
// { if(this.state != LoginState.INIT)
// if(this.state != LoginState.HELLO) throw new IllegalStateException("Unerwartetes Login-Paket");
// throw new IllegalStateException("Unerwartetes Start-Paket"); this.state = LoginState.ENCRYPT;
// if(!this.netManager.isLocalChannel()) { this.loginToken = new byte[4];
// this.state = LoginState.PASSWORD; TOKEN_RNG.nextBytes(this.loginToken);
// this.netManager.sendPacket(new RPacketPasswordRequest()); // !Config.password.isEmpty(), Config.auth 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) { public void processPasswordResponse(LPacketPasswordResponse packetIn) {
if(this.state != LoginState.PASSWORD) if(this.state != LoginState.PASSWORD)
@ -116,6 +138,6 @@ public class LoginHandler extends NetHandler implements ILoginHandler
this.closeConnection("Falsches Zugangspasswort"); this.closeConnection("Falsches Zugangspasswort");
return; return;
} }
this.state = LoginState.READY_TO_ACCEPT; this.state = LoginState.AUTHENTICATED;
} }
} }