add encryption and enforce authentication
This commit is contained in:
parent
2ea3267e3a
commit
8d4b4b3619
16 changed files with 452 additions and 73 deletions
|
@ -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;
|
||||
|
|
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.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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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<Packet>
|
|||
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<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
|
||||
{
|
||||
|
|
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.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);
|
||||
}
|
||||
},
|
||||
|
|
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue