mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-11 00:23:42 -04:00
basic (very wip) mojang api, protocol encryption, login
This commit is contained in:
parent
34270f564f
commit
c044abbdc3
6
src/main/java/de/bixilon/minosoft/Config.java
Normal file
6
src/main/java/de/bixilon/minosoft/Config.java
Normal file
@ -0,0 +1,6 @@
|
||||
package de.bixilon.minosoft;
|
||||
|
||||
public class Config {
|
||||
public static String username = ""; // mojang email
|
||||
public static String password = "";
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
73
src/main/java/de/bixilon/minosoft/objects/Account.java
Normal file
73
src/main/java/de/bixilon/minosoft/objects/Account.java
Normal file
@ -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<String> 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<String> 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;
|
||||
}
|
||||
}
|
24
src/main/java/de/bixilon/minosoft/objects/Player.java
Normal file
24
src/main/java/de/bixilon/minosoft/objects/Player.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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<ClientboundPacket> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<byte[]> binQueue;
|
||||
private final List<byte[]> 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<Byte> 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<? extends ClientboundPacket> 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!");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
28
src/main/java/de/bixilon/minosoft/util/HTTP.java
Normal file
28
src/main/java/de/bixilon/minosoft/util/HTTP.java
Normal file
@ -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<String> 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;
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user