add encryption and enforce authentication
This commit is contained in:
parent
2ea3267e3a
commit
8d4b4b3619
16 changed files with 452 additions and 73 deletions
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
41
common/src/common/network/EncryptionCodec.java
Normal file
41
common/src/common/network/EncryptionCodec.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
20
common/src/common/network/NettyEncryptionDecoder.java
Normal file
20
common/src/common/network/NettyEncryptionDecoder.java
Normal 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_));
|
||||||
|
}
|
||||||
|
}
|
19
common/src/common/network/NettyEncryptionEncoder.java
Normal file
19
common/src/common/network/NettyEncryptionEncoder.java
Normal 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_);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
21
common/src/common/packet/LPacketLogin.java
Normal file
21
common/src/common/packet/LPacketLogin.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
47
common/src/common/packet/LPacketStartEncrypt.java
Normal file
47
common/src/common/packet/LPacketStartEncrypt.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
44
common/src/common/packet/RPacketRequestEncrypt.java
Normal file
44
common/src/common/packet/RPacketRequestEncrypt.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
135
common/src/common/util/EncryptUtil.java
Normal file
135
common/src/common/util/EncryptUtil.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue