From 2744e7cf6c82273630d637ead05e2d6e68e1afcd Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Wed, 27 May 2015 13:35:44 +0200 Subject: [PATCH] Replaced insecure Minecraft account authentication with server join technique --- .../crushedpixel/replaymod/api/ApiClient.java | 24 ++++++++-- .../api/mojang/MojangApiMethods.java | 1 + .../replaymod/gui/online/GuiRegister.java | 4 ++ .../authentication/AuthenticationHandler.java | 7 +-- .../authentication/AuthenticationHash.java | 44 +++++++++++++++++++ .../assets/replaymod/lang/en_US.lang | 1 + 6 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHash.java diff --git a/src/main/java/eu/crushedpixel/replaymod/api/ApiClient.java b/src/main/java/eu/crushedpixel/replaymod/api/ApiClient.java index b9d34a77..353025f4 100755 --- a/src/main/java/eu/crushedpixel/replaymod/api/ApiClient.java +++ b/src/main/java/eu/crushedpixel/replaymod/api/ApiClient.java @@ -4,12 +4,15 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; +import com.mojang.authlib.exceptions.AuthenticationException; import eu.crushedpixel.replaymod.api.mojang.MojangApiMethods; import eu.crushedpixel.replaymod.api.mojang.holders.Profile; import eu.crushedpixel.replaymod.api.replay.ReplayModApiMethods; import eu.crushedpixel.replaymod.api.replay.SearchQuery; import eu.crushedpixel.replaymod.api.replay.holders.*; +import eu.crushedpixel.replaymod.online.authentication.AuthenticationHash; import eu.crushedpixel.replaymod.utils.StreamTools; +import net.minecraft.client.Minecraft; import org.apache.commons.io.FileUtils; import java.io.File; @@ -24,6 +27,7 @@ import java.util.UUID; public class ApiClient { + private static final Minecraft mc = Minecraft.getMinecraft(); private static final Gson gson = new Gson(); private static final JsonParser jsonParser = new JsonParser(); @@ -36,18 +40,32 @@ public class ApiClient { return auth; } - public AuthKey register(String username, String mail, String password, String uuid, String accessToken) - throws IOException, ApiException { + public AuthKey register(String username, String mail, String password, String uuid) + throws IOException, ApiException, AuthenticationException { + + AuthenticationHash authenticationHash = sessionserverJoin(); + QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.register); builder.put("username", username); builder.put("email", mail); builder.put("password", password); builder.put("uuid", uuid); - builder.put("accesstoken", accessToken); + builder.put("mcusername", authenticationHash.username); + builder.put("timelong", authenticationHash.currentTime); + builder.put("randomlong", authenticationHash.randomLong); AuthKey auth = invokeAndReturn(builder, AuthKey.class); return auth; } + private AuthenticationHash sessionserverJoin() throws AuthenticationException { + AuthenticationHash hash = new AuthenticationHash(); + + mc.getSessionService().joinServer( + mc.getSession().getProfile(), mc.getSession().getToken(), hash.hash); + + return hash; + } + public AuthConfirmation checkAuthkey(String auth) { try { QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.check_authkey); diff --git a/src/main/java/eu/crushedpixel/replaymod/api/mojang/MojangApiMethods.java b/src/main/java/eu/crushedpixel/replaymod/api/mojang/MojangApiMethods.java index 9ca93db5..73ffa9bd 100755 --- a/src/main/java/eu/crushedpixel/replaymod/api/mojang/MojangApiMethods.java +++ b/src/main/java/eu/crushedpixel/replaymod/api/mojang/MojangApiMethods.java @@ -3,5 +3,6 @@ package eu.crushedpixel.replaymod.api.mojang; public class MojangApiMethods { public static final String userprofile = "https://sessionserver.mojang.com/session/minecraft/profile/"; + public static final String joinServer = "https://sessionserver.mojang.com/session/minecraft/join"; } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/online/GuiRegister.java b/src/main/java/eu/crushedpixel/replaymod/gui/online/GuiRegister.java index d48e654c..d25cec4f 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/online/GuiRegister.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/online/GuiRegister.java @@ -1,5 +1,6 @@ package eu.crushedpixel.replaymod.gui.online; +import com.mojang.authlib.exceptions.AuthenticationException; import eu.crushedpixel.replaymod.api.ApiException; import eu.crushedpixel.replaymod.gui.GuiConstants; import eu.crushedpixel.replaymod.gui.PasswordTextField; @@ -196,6 +197,9 @@ public class GuiRegister extends GuiScreen { }); } catch(ApiException ae) { message = ae.getLocalizedMessage(); + } catch(AuthenticationException aue) { + aue.printStackTrace(); + message = I18n.format("replaymod.gui.register.error.authfailed"); } catch(Exception e) { e.printStackTrace(); message = I18n.format("replaymod.gui.login.connectionerror"); diff --git a/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHandler.java b/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHandler.java index 2d80ee05..fa339025 100755 --- a/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHandler.java @@ -1,5 +1,6 @@ package eu.crushedpixel.replaymod.online.authentication; +import com.mojang.authlib.exceptions.AuthenticationException; import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.api.ApiClient; import eu.crushedpixel.replaymod.api.ApiException; @@ -36,10 +37,10 @@ public class AuthenticationHandler { private static Minecraft mc = Minecraft.getMinecraft(); - public static void register(String usrname, String mail, String password) throws IOException, ApiException { + public static void register(String usrname, String mail, String password) + throws IOException, ApiException, AuthenticationException { AuthKey auth = apiClient.register(usrname, mail, password, - mc.getSession().getProfile().getId().toString(), - Minecraft.getMinecraft().getSession().getToken()); + mc.getSession().getProfile().getId().toString()); username = usrname; authkey = auth.getAuthkey(); saveAuthkey(authkey); diff --git a/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHash.java b/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHash.java new file mode 100644 index 00000000..0df1d80b --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/online/authentication/AuthenticationHash.java @@ -0,0 +1,44 @@ +package eu.crushedpixel.replaymod.online.authentication; + +import net.minecraft.client.Minecraft; + +import java.util.Random; + +public class AuthenticationHash { + + private static final Random random = new Random(); + + public AuthenticationHash() { + username = Minecraft.getMinecraft().getSession().getUsername(); + currentTime = System.currentTimeMillis(); + randomLong = random.nextLong(); + hash = getAuthenticationHash(); + } + + public final String username; + public final long currentTime; + public final long randomLong; + public final String hash; + + private String getAuthenticationHash() { + StringBuilder builder = new StringBuilder(); + builder.append(username); + builder.append(currentTime); + builder.append(randomLong); + + String md5 = builder.toString(); + + try { + java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); + byte[] array = md.digest(md5.getBytes()); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < array.length; ++i) { + sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3)); + } + return sb.toString(); + } catch (java.security.NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/resources/assets/replaymod/lang/en_US.lang b/src/main/resources/assets/replaymod/lang/en_US.lang index 8b0bcb25..39441390 100644 --- a/src/main/resources/assets/replaymod/lang/en_US.lang +++ b/src/main/resources/assets/replaymod/lang/en_US.lang @@ -112,6 +112,7 @@ replaymod.gui.register.confirmpw=Confirm Password replaymod.gui.register.error.nomatch=Passwords don't match replaymod.gui.register.error.shortusername=Username has to be at least 5 characters long replaymod.gui.register.error.shortpw=Password has to be at least 5 characters long +replaymod.gui.register.error.authfailed=Could not authenticate your Minecraft account #Replay Viewer GUI