diff --git a/src/main/java/de/bixilon/minosoft/Config.java b/src/main/java/de/bixilon/minosoft/Config.java new file mode 100644 index 000000000..60df37037 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/Config.java @@ -0,0 +1,6 @@ +package de.bixilon.minosoft; + +public class Config { + public static String username = ""; // mojang email + public static String password = ""; +} diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.java b/src/main/java/de/bixilon/minosoft/Minosoft.java index fb2c6c4cc..b688aea1a 100644 --- a/src/main/java/de/bixilon/minosoft/Minosoft.java +++ b/src/main/java/de/bixilon/minosoft/Minosoft.java @@ -7,6 +7,6 @@ public class Minosoft { public static void main(String[] args) { Log.info("Starting..."); Connection c = new Connection("127.0.0.1", 25565); - c.ping(); + c.connect(); } } diff --git a/src/main/java/de/bixilon/minosoft/logging/Log.java b/src/main/java/de/bixilon/minosoft/logging/Log.java index 127fcdaf9..6499233e5 100644 --- a/src/main/java/de/bixilon/minosoft/logging/Log.java +++ b/src/main/java/de/bixilon/minosoft/logging/Log.java @@ -3,7 +3,7 @@ package de.bixilon.minosoft.logging; import java.text.SimpleDateFormat; public class Log { - static LogLevel level = LogLevel.VERBOSE; + static LogLevel level = LogLevel.PROTOCOL; static SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); public static void log(LogLevel l, String message) { diff --git a/src/main/java/de/bixilon/minosoft/objects/Account.java b/src/main/java/de/bixilon/minosoft/objects/Account.java new file mode 100644 index 000000000..8527f4952 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/objects/Account.java @@ -0,0 +1,73 @@ +package de.bixilon.minosoft.objects; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.util.HTTP; +import de.bixilon.minosoft.util.Util; +import org.json.JSONObject; + +import java.net.http.HttpResponse; +import java.util.UUID; + +public class Account { + + String username; + String password; + String playerName; + + String token; + UUID uuid; + + public Account(String username, String password) { + this.username = username; + this.password = password; + } + + public void login() { + JSONObject payload = new JSONObject(); + payload.put("agent", new JSONObject().put("name", "Minecraft").put("version", 1)); + payload.put("username", username); + payload.put("password", password); + // ToDo not in main thread + HttpResponse response = HTTP.postJson("https://authserver.mojang.com/authenticate", payload); + if (response == null || response.statusCode() != 200) { + assert response != null; + Log.info(String.format("[Mojang API] Login failed with username=%s (%s)", username, response.statusCode())); + return; + } + + + Log.info(String.format("[Mojang API] Login successful with username=%s", username)); + + // login good + JSONObject raw = new JSONObject(response.body()); + + token = raw.getString("accessToken"); + + uuid = Util.formatUUID(raw.getJSONObject("selectedProfile").getString("id")); + playerName = raw.getJSONObject("selectedProfile").getString("name"); + } + + public void join(String serverId) { + JSONObject payload = new JSONObject(); + payload.put("accessToken", token); + payload.put("selectedProfile", getUUID().toString().replace("-", "")); + payload.put("serverId", serverId); + // ToDo not in main thread + HttpResponse response = HTTP.postJson("https://sessionserver.mojang.com/session/minecraft/join", payload); + if (response == null || response.statusCode() != 204) { + assert response != null; + Log.info("[Mojang API] Login to join server!"); + return; + } + + Log.info("[Mojang API] Joined server successfully"); + } + + public UUID getUUID() { + return this.uuid; + } + + public String getPlayerName() { + return this.playerName; + } +} diff --git a/src/main/java/de/bixilon/minosoft/objects/Player.java b/src/main/java/de/bixilon/minosoft/objects/Player.java new file mode 100644 index 000000000..b172efa7c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/objects/Player.java @@ -0,0 +1,24 @@ +package de.bixilon.minosoft.objects; + +import java.util.UUID; + +public class Player { + Account acc; + + public Player(Account acc) { + this.acc = acc; + acc.login(); + } + + public String getPlayerName() { + return acc.getPlayerName(); + } + + public UUID getPlayerUUID() { + return acc.getUUID(); + } + + public Account getAccount() { + return this.acc; + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java index a571b63da..f1bb69c56 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java +++ b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java @@ -1,8 +1,13 @@ package de.bixilon.minosoft.protocol.network; +import de.bixilon.minosoft.Config; import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.objects.Account; +import de.bixilon.minosoft.objects.Player; import de.bixilon.minosoft.protocol.packets.ClientboundPacket; +import de.bixilon.minosoft.protocol.packets.ServerboundPacket; import de.bixilon.minosoft.protocol.packets.serverbound.handshaking.PacketHandshake; +import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketLoginStart; import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusPing; import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusRequest; import de.bixilon.minosoft.protocol.protocol.ConnectionState; @@ -18,6 +23,7 @@ public class Connection { private final Network network; private final PacketHandler handler; private final ArrayList handlingQueue; + private Player player = new Player(new Account(Config.username, Config.password)); private ConnectionState state = ConnectionState.DISCONNECTED; private boolean onlyPing; @@ -88,6 +94,9 @@ public class Connection { network.sendPacket(new PacketStatusRequest()); network.sendPacket(new PacketStatusPing(0)); break; + case LOGIN: + network.sendPacket(new PacketLoginStart(player)); + break; } } @@ -111,4 +120,12 @@ public class Connection { public void disconnect() { setConnectionState(ConnectionState.DISCONNECTING); } + + public Player getPlayer() { + return player; + } + + public void sendPacket(ServerboundPacket p) { + network.sendPacket(p); + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/Network.java b/src/main/java/de/bixilon/minosoft/protocol/network/Network.java index ebc47cfb5..a3fe1799a 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/Network.java +++ b/src/main/java/de/bixilon/minosoft/protocol/network/Network.java @@ -3,11 +3,14 @@ package de.bixilon.minosoft.protocol.network; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.protocol.packets.ClientboundPacket; import de.bixilon.minosoft.protocol.packets.ServerboundPacket; -import de.bixilon.minosoft.protocol.protocol.ConnectionState; -import de.bixilon.minosoft.protocol.protocol.InPacketBuffer; -import de.bixilon.minosoft.protocol.protocol.Protocol; +import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse; +import de.bixilon.minosoft.protocol.protocol.*; import de.bixilon.minosoft.util.Util; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.SecretKey; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -22,6 +25,9 @@ public class Network { private final List binQueue; private final List binQueueIn; private Socket socket; + private boolean encryptionEnabled = false; + private Cipher cipherEncrypt; + private Cipher cipherDecrypt; public Network(Connection c) { this.connection = c; @@ -62,29 +68,37 @@ public class Network { binQueue.remove(0); } - // everything sent for now, waiting for data - - while (dIn.available() > 0) { - int numRead = 0; - int len = 0; - byte read; - do { - read = dIn.readByte(); - int value = (read & 0b01111111); - len |= (value << (7 * numRead)); - - numRead++; - if (numRead > 5) { - throw new RuntimeException("VarInt is too big"); - } - } while ((read & 0b10000000) != 0); - - - byte[] in = dIn.readNBytes(len); - // add to queue - binQueueIn.add(in); - + if (dIn.available() == 0) { + // nothing to receive + Util.sleep(1); + continue; } + // everything sent for now, waiting for data + List raw = new ArrayList<>(); + byte[] buffer = new byte[1]; + while (true) { + if (raw.size() > ProtocolDefinition.PROTOCOL_PACKET_MAX_SIZE) { + raw = null; + break; + } + if (dIn.available() == 0) { + // packet end + break; + } + dIn.readFully(buffer, 0, 1); + raw.add(buffer[0]); + } + if (raw == null || raw.size() == 0) { + // data was tto long, ... + continue; + } + // convert to array + byte[] in = new byte[raw.size()]; + for (int i = 0; i < raw.size(); i++) { + in[i] = raw.get(i); + } + // add to queue + binQueueIn.add(in); Util.sleep(1); } @@ -107,17 +121,52 @@ public class Network { while (queue.size() > 0) { ServerboundPacket p = queue.get(0); - binQueue.add(p.write(connection.getVersion()).getOutBytes()); + byte[] raw = p.write(connection.getVersion()).getOutBytes(); + if (encryptionEnabled) { + // encrypt + byte[] encrypted; + try { + encrypted = cipherEncrypt.doFinal(raw); + } catch (IllegalBlockSizeException | BadPaddingException e) { + Log.fatal("Failed to encrypt!"); + e.printStackTrace(); + binQueue.remove(0); + continue; + } + binQueue.add(encrypted); + } else { + binQueue.add(raw); + } queue.remove(0); + if (p instanceof PacketEncryptionResponse) { + // enable encryption + enableEncryption(((PacketEncryptionResponse) p).getSecretKey()); + } } while (binQueueIn.size() > 0) { // read data - InPacketBuffer inPacketBuffer = new InPacketBuffer(binQueueIn.get(0)); + byte[] raw = binQueueIn.get(0); + InPacketBuffer inPacketBuffer; + if (encryptionEnabled) { + // decrypt + byte[] decrypted; + try { + decrypted = cipherDecrypt.doFinal(raw); + } catch (IllegalBlockSizeException | BadPaddingException e) { + Log.fatal("Failed to decrypt!"); + e.printStackTrace(); + binQueueIn.remove(0); + continue; + } + inPacketBuffer = new InPacketBuffer(decrypted); + } else { + inPacketBuffer = new InPacketBuffer(raw); + } Class clazz = Protocol.getPacketByPacket(connection.getVersion().getProtocol().getPacketByCommand(connection.getConnectionState(), inPacketBuffer.getCommand())); if (clazz == null) { - Log.warn("[IN] Unknown: " + inPacketBuffer.getCommand()); + Log.warn("[IN] Unknown packet with command " + inPacketBuffer.getCommand()); binQueueIn.remove(0); continue; } @@ -143,6 +192,13 @@ public class Network { public void sendPacket(ServerboundPacket p) { queue.add(p); + } + public void enableEncryption(SecretKey secretKey) { + Log.debug("Enabling encryption..."); + cipherEncrypt = CryptManager.createNetCipherInstance(Cipher.ENCRYPT_MODE, secretKey); + cipherDecrypt = CryptManager.createNetCipherInstance(Cipher.DECRYPT_MODE, secretKey); + encryptionEnabled = true; + Log.debug("Encryption enabled!"); } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketEncryptionKeyRequest.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketEncryptionKeyRequest.java new file mode 100644 index 000000000..e42eb328c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketEncryptionKeyRequest.java @@ -0,0 +1,48 @@ +package de.bixilon.minosoft.protocol.packets.clientbound.login; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.protocol.packets.ClientboundPacket; +import de.bixilon.minosoft.protocol.protocol.InPacketBuffer; +import de.bixilon.minosoft.protocol.protocol.PacketHandler; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +public class PacketEncryptionKeyRequest implements ClientboundPacket { + String serverId; //normally empty + byte[] publicKey; + byte[] verifyToken; + + @Override + public void read(InPacketBuffer buffer, ProtocolVersion v) { + switch (v) { + case VERSION_1_7_10: + serverId = buffer.readString(); + publicKey = buffer.readBytes(buffer.readShort()); // read length, then the bytes + verifyToken = buffer.readBytes(buffer.readShort()); // read length, then the bytes + break; + } // ToDo + + log(); + } + + @Override + public void log() { + Log.protocol("Receiving encryption request packet"); + } + + @Override + public void handle(PacketHandler h) { + h.handle(this); + } + + public byte[] getPublicKey() { + return publicKey; + } + + public byte[] getVerifyToken() { + return verifyToken; + } + + public String getServerId() { + return serverId; + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketLoginSuccess.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketLoginSuccess.java new file mode 100644 index 000000000..7e27d0396 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/login/PacketLoginSuccess.java @@ -0,0 +1,40 @@ +package de.bixilon.minosoft.protocol.packets.clientbound.login; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.protocol.packets.ClientboundPacket; +import de.bixilon.minosoft.protocol.protocol.InPacketBuffer; +import de.bixilon.minosoft.protocol.protocol.PacketHandler; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +import java.util.UUID; + +public class PacketLoginSuccess implements ClientboundPacket { + UUID uuid; + String username; + + @Override + public void read(InPacketBuffer buffer, ProtocolVersion v) { + uuid = UUID.fromString(buffer.readString()); + username = buffer.readString(); + + log(); + } + + @Override + public void log() { + Log.protocol("Receiving login success packet"); + } + + @Override + public void handle(PacketHandler h) { + h.handle(this); + } + + public UUID getUUID() { + return uuid; + } + + public String getUsername() { + return username; + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketEncryptionResponse.java b/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketEncryptionResponse.java new file mode 100644 index 000000000..b4923b132 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketEncryptionResponse.java @@ -0,0 +1,50 @@ +package de.bixilon.minosoft.protocol.packets.serverbound.login; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.protocol.packets.ServerboundPacket; +import de.bixilon.minosoft.protocol.protocol.CryptManager; +import de.bixilon.minosoft.protocol.protocol.OutPacketBuffer; +import de.bixilon.minosoft.protocol.protocol.Packets; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +import javax.crypto.SecretKey; +import java.security.PublicKey; + +public class PacketEncryptionResponse implements ServerboundPacket { + + byte[] secret; + byte[] token; + SecretKey secretKey; + + + public PacketEncryptionResponse(SecretKey secret, byte[] token, PublicKey key) { + this.secretKey = secret; + this.secret = CryptManager.encryptData(key, secret.getEncoded()); + this.token = CryptManager.encryptData(key, token); + } + + + public SecretKey getSecretKey() { + return secretKey; + } + + @Override + public OutPacketBuffer write(ProtocolVersion v) { + log(); + OutPacketBuffer buffer = new OutPacketBuffer(v.getPacketCommand(Packets.Serverbound.LOGIN_ENCRYPTION_RESPONSE)); + switch (v) { + case VERSION_1_7_10: + buffer.writeShort((short) secret.length); + buffer.writeBytes(secret); + buffer.writeShort((short) token.length); + buffer.writeBytes(token); + } + //buffer.writeString(username); + return buffer; + } + + @Override + public void log() { + Log.protocol("Sending encryption response"); + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketLoginStart.java b/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketLoginStart.java new file mode 100644 index 000000000..65819453b --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/serverbound/login/PacketLoginStart.java @@ -0,0 +1,35 @@ +package de.bixilon.minosoft.protocol.packets.serverbound.login; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.objects.Player; +import de.bixilon.minosoft.protocol.packets.ServerboundPacket; +import de.bixilon.minosoft.protocol.protocol.OutPacketBuffer; +import de.bixilon.minosoft.protocol.protocol.Packets; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +public class PacketLoginStart implements ServerboundPacket { + + private final String username; + + public PacketLoginStart(Player p) { + username = p.getPlayerName(); + } + + public PacketLoginStart(String username) { + this.username = username; + } + + @Override + public OutPacketBuffer write(ProtocolVersion v) { + log(); + // no version checking, is the same in all versions (1.7.x - 1.15.2) + OutPacketBuffer buffer = new OutPacketBuffer(v.getPacketCommand(Packets.Serverbound.LOGIN_LOGIN_START)); + buffer.writeString(username); + return buffer; + } + + @Override + public void log() { + Log.protocol(String.format("Sending login start (%s)", username)); + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/CryptManager.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/CryptManager.java new file mode 100644 index 000000000..d61880ab8 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/CryptManager.java @@ -0,0 +1,109 @@ +package de.bixilon.minosoft.protocol.protocol; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +public class CryptManager { + // little thanks to https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/net/minecraft/util/CryptManager.html + public static SecretKey createNewSharedKey() { + try { + KeyGenerator key = KeyGenerator.getInstance("AES"); + key.init(128); + return key.generateKey(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + public static KeyPair createNewKeyPair() { + try { + KeyPairGenerator keyPair = KeyPairGenerator.getInstance("RSA"); + keyPair.initialize(1024); + return keyPair.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + public static byte[] getServerHash(String serverId, PublicKey publicKey, SecretKey secretKey) { + try { + return digestOperation(serverId.getBytes("ISO_8859_1"), secretKey.getEncoded(), publicKey.getEncoded()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return null; + } + } + + private static byte[] digestOperation(byte[]... bytes) { + try { + MessageDigest disgest = MessageDigest.getInstance("SHA-1"); + for (byte[] b : bytes) { + disgest.update(b); + } + return disgest.digest(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + public static PublicKey decodePublicKey(byte[] key) { + try { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + e.printStackTrace(); + } + return null; + } + + public static SecretKey decryptSharedKey(PrivateKey key, byte[] data) { + return new SecretKeySpec(decryptData(key, data), "AES"); + } + + public static byte[] encryptData(Key key, byte[] data) { + return cipherOperation(1, key, data); + } + + public static byte[] decryptData(Key key, byte[] data) { + return cipherOperation(2, key, data); + } + + private static byte[] cipherOperation(int p_75885_0_, Key key, byte[] data) { + try { + return createTheCipherInstance(p_75885_0_, key.getAlgorithm(), key).doFinal(data); + } catch (IllegalBlockSizeException | BadPaddingException e) { + e.printStackTrace(); + } + return null; + } + + private static Cipher createTheCipherInstance(int p_75886_0_, String transformation, Key key) { + try { + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(p_75886_0_, key); + return cipher; + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + e.printStackTrace(); + } + return null; + } + + public static Cipher createNetCipherInstance(int opMode, Key key) { + try { + Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding"); + cipher.init(opMode, key, new IvParameterSpec(key.getEncoded())); + return cipher; + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/InPacketBuffer.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/InPacketBuffer.java index 795a3d20b..f3137bd0f 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/InPacketBuffer.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/InPacketBuffer.java @@ -1,15 +1,13 @@ package de.bixilon.minosoft.protocol.protocol; -import java.util.ArrayList; -import java.util.List; - public class InPacketBuffer extends InByteBuffer { private final int command; + private final int length; // not interested in yet public InPacketBuffer(byte[] bytes) { super(bytes); // ToDo: compression - + length = readVarInt(); command = readVarInt(); } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java index e691aad15..63d241f23 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java @@ -2,8 +2,15 @@ package de.bixilon.minosoft.protocol.protocol; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.protocol.network.Connection; +import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketEncryptionKeyRequest; +import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginSuccess; import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusPong; import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusResponse; +import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse; + +import javax.crypto.SecretKey; +import java.math.BigInteger; +import java.security.PublicKey; public class PacketHandler { Connection connection; @@ -14,7 +21,6 @@ public class PacketHandler { public void handle(PacketStatusResponse pkg) { Log.info(String.format("Status response received: %s/%s online. MotD: '%s'", pkg.getResponse().getPlayerOnline(), pkg.getResponse().getMaxPlayers(), pkg.getResponse().getMotd())); - } public void handle(PacketStatusPong pkg) { @@ -22,7 +28,20 @@ public class PacketHandler { if (connection.isOnlyPing()) { // pong arrived, closing connection connection.disconnect(); - } } + + public void handle(PacketEncryptionKeyRequest pkg) { + SecretKey secretKey = CryptManager.createNewSharedKey(); + PublicKey publicKey = CryptManager.decodePublicKey(pkg.getPublicKey()); + String serverHash = new BigInteger(CryptManager.getServerHash(pkg.getServerId(), publicKey, secretKey)).toString(16); + connection.getPlayer().getAccount().join(serverHash); + connection.sendPacket(new PacketEncryptionResponse(secretKey, pkg.getVerifyToken(), publicKey)); + + } + + public void handle(PacketLoginSuccess pkg) { + // now we are playing + connection.setConnectionState(ConnectionState.PLAY); + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java index 8906b1035..c67e2a32a 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java @@ -1,6 +1,8 @@ package de.bixilon.minosoft.protocol.protocol; import de.bixilon.minosoft.protocol.packets.ClientboundPacket; +import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketEncryptionKeyRequest; +import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginSuccess; import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusPong; import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusResponse; @@ -26,5 +28,7 @@ public interface Protocol { private static void initPacketClassMapping() { packetClassMapping.put(Packets.Clientbound.STATUS_RESPONSE, PacketStatusResponse.class); packetClassMapping.put(Packets.Clientbound.STATUS_PONG, PacketStatusPong.class); + packetClassMapping.put(Packets.Clientbound.LOGIN_ENCRYPTION_REQUEST, PacketEncryptionKeyRequest.class); + packetClassMapping.put(Packets.Clientbound.LOGIN_LOGIN_SUCCESS, PacketLoginSuccess.class); } } \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/util/HTTP.java b/src/main/java/de/bixilon/minosoft/util/HTTP.java new file mode 100644 index 000000000..f19b12058 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/HTTP.java @@ -0,0 +1,28 @@ +package de.bixilon.minosoft.util; + +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public class HTTP { + + public static HttpResponse postJson(String url, JSONObject json) { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .POST(HttpRequest.BodyPublishers.ofString(json.toString())) + .header("Content-Type", "application/json") + .build(); + try { + return client.send(request, + HttpResponse.BodyHandlers.ofString()); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index 189d3a4bf..4c8593f47 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -1,12 +1,21 @@ package de.bixilon.minosoft.util; +import java.util.UUID; +import java.util.regex.Pattern; + public class Util { + private static final Pattern UUID_FIX = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); + // thanks https://www.spigotmc.org/threads/free-code-easily-convert-between-trimmed-and-full-uuids.165615 + public static void sleep(int ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); } + } + public static UUID formatUUID(String uuid) { + return UUID.fromString(UUID_FIX.matcher(uuid.replace("-", "")).replaceAll("$1-$2-$3-$4-$5")); } }