diff options
author | syeyoung <42869671+cyoung06@users.noreply.github.com> | 2020-12-08 14:54:17 +0900 |
---|---|---|
committer | syeyoung <42869671+cyoung06@users.noreply.github.com> | 2020-12-08 14:54:17 +0900 |
commit | 87e89fd42ca44400a3d3d3d5477cd1525b24d91a (patch) | |
tree | 02023a2650c2a9e4fd2d47f64cb91ec614a28556 | |
parent | 055520e1d8313a302f58babc6526d95bf22b471f (diff) | |
download | Skyblock-Dungeons-Guide-87e89fd42ca44400a3d3d3d5477cd1525b24d91a.tar.gz Skyblock-Dungeons-Guide-87e89fd42ca44400a3d3d3d5477cd1525b24d91a.tar.bz2 Skyblock-Dungeons-Guide-87e89fd42ca44400a3d3d3d5477cd1525b24d91a.zip |
authentication stuff and network loading
7 files changed, 333 insertions, 8 deletions
diff --git a/build.gradle b/build.gradle index 6ed97f83..a1260803 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,7 @@ minecraft { dependencies { implementation 'org.jetbrains:annotations-java5:19.0.0' + implementation 'com.auth0:java-jwt:3.11.0' // you may put jars on which you depend on in ./libs // or you may define them like so.. diff --git a/src/main/java/kr/syeyoung/dungeonsguide/Authenticator.java b/src/main/java/kr/syeyoung/dungeonsguide/Authenticator.java new file mode 100644 index 00000000..15b238c6 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/Authenticator.java @@ -0,0 +1,133 @@ +package kr.syeyoung.dungeonsguide; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import lombok.Getter; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Session; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.*; +import java.security.*; +import java.util.UUID; + +public class Authenticator { + @Getter + private KeyPair keyPair; + @Getter + private String token; + private KeyPair generate1024RSAKey() { + KeyPairGenerator generator = null; + try { + generator = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + generator.initialize(1024); + keyPair = generator.generateKeyPair(); + return keyPair; + } + + public Authenticator() { + generate1024RSAKey(); + } + + private static final String DOMAIN = "http://localhost:8080/"; + + public String authenticate() throws IOException, AuthenticationException, NoSuchAlgorithmException { + Session session = Minecraft.getMinecraft().getSession(); + String token = session.getToken(); + + String jwt = requestAuth(session.getProfile()); + MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService(); + DecodedJWT jwt2 = JWT.decode(jwt); + String hash = calculateAuthHash(DatatypeConverter.parseBase64Binary(jwt2.getClaim("sharedSecret").asString()), + DatatypeConverter.parseBase64Binary(jwt2.getClaim("publicKey").asString())); + yggdrasilMinecraftSessionService.joinServer(session.getProfile(), token, hash); + token = requestAuth2(jwt, keyPair.getPublic()); + return token; + } + + private String requestAuth(GameProfile profile) throws IOException { + HttpURLConnection huc = (HttpURLConnection) new URL(DOMAIN+"auth/requestAuth").openConnection(); + huc.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + huc.setRequestProperty("Content-Type", "application/json"); + huc.setRequestMethod("POST"); + huc.setDoInput(true); + huc.setDoOutput(true); + + huc.getOutputStream().write(("{\"uuid\":\""+profile.getId().toString()+"\",\"nickname\":\""+profile.getName()+"\"}").getBytes()); + InputStreamReader inputStreamReader = new InputStreamReader(huc.getInputStream()); + JsonObject object = (JsonObject) new JsonParser().parse(inputStreamReader); + if (!"ok".equals(object.get("status").getAsString())) { + return null; + } + return object.get("data").getAsString(); + } + private String requestAuth2(String token, PublicKey publicKey) throws IOException { + HttpURLConnection huc = (HttpURLConnection) new URL(DOMAIN+"auth/authenticate").openConnection(); + huc.setRequestMethod("POST"); + huc.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + huc.setRequestProperty("Content-Type", "application/json"); + huc.setDoInput(true); + huc.setDoOutput(true); + + huc.getOutputStream().write(("{\"jwt\":\""+token+"\",\"publicKey\":\""+DatatypeConverter.printBase64Binary(publicKey.getEncoded())+"\"}").getBytes()); + InputStreamReader inputStreamReader = new InputStreamReader(huc.getInputStream()); + JsonObject object = (JsonObject) new JsonParser().parse(inputStreamReader); + if (!"ok".equals(object.get("status").getAsString())) { + return null; + } + return object.get("data").getAsString(); + } + public String calculateAuthHash(byte[] sharedSecret, byte[] pk) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update("".getBytes()); + md.update(sharedSecret); + md.update(pk); + byte[] result = md.digest(); + return new BigInteger(result).toString(16); + } + public InputStream getInputStream(String resource) throws IOException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException { + HttpURLConnection huc = (HttpURLConnection) new URL(DOMAIN+"resource/resource?class="+ URLEncoder.encode(resource)).openConnection(); + huc.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + huc.setRequestProperty("Content-Type", "application/json"); + huc.setRequestProperty("Authorization", token); + huc.setDoInput(true); + huc.setDoOutput(true); + + InputStream inputStream = huc.getInputStream(); + byte[] bytes = new byte[4]; + inputStream.read(bytes); + int len = ((bytes[0] & 0xFF) << 24) | + ((bytes[1] & 0xFF) << 16) | + ((bytes[2] & 0xFF) << 8 ) | + ((bytes[3] & 0xFF)); + + byte[] pubKey = new byte[len]; + inputStream.read(pubKey); + + Cipher cipher = Cipher.getInstance("RSA"); + byte[] byteEncrypted = pubKey; + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + byte[] bytePlain = cipher.doFinal(byteEncrypted); + + cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKeySpec keySpec = new SecretKeySpec(bytePlain, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(bytePlain); + cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec); + return new CipherInputStream(inputStream, cipher); + } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/DungeonsGuide.java b/src/main/java/kr/syeyoung/dungeonsguide/DungeonsGuide.java index bcbcbe0a..de1b0067 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/DungeonsGuide.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/DungeonsGuide.java @@ -1,21 +1,32 @@ package kr.syeyoung.dungeonsguide; +import com.mojang.authlib.exceptions.AuthenticationException; import kr.syeyoung.dungeonsguide.commands.*; import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoomInfoRegistry; import lombok.Getter; import net.minecraft.client.Minecraft; -import net.minecraft.command.CommandDebug; -import net.minecraft.init.Blocks; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiErrorScreen; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.IChatComponent; import net.minecraftforge.client.ClientCommandHandler; +import net.minecraftforge.client.event.GuiOpenEvent; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.CustomModLoadingErrorDisplayException; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.io.IOUtils; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import java.io.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; @Mod(modid = DungeonsGuide.MODID, version = DungeonsGuide.VERSION) public class DungeonsGuide @@ -72,7 +83,23 @@ public class DungeonsGuide } } } - DungeonRoomInfoRegistry.loadAll(configDir); + try { + DungeonRoomInfoRegistry.loadAll(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } Keybinds.register(); } @@ -88,9 +115,66 @@ public class DungeonsGuide @Getter private File configDir; + @Getter + private Authenticator authenticator; + private NetworkClassLoader classLoader; @EventHandler public void pre(FMLPreInitializationEvent event) { configDir = new File(event.getModConfigurationDirectory(),"dungeonsguide"); + + authenticator = new Authenticator(); + String token = null; + try { + token = authenticator.authenticate(); + System.out.println(token); + if (token != null) { + classLoader = new NetworkClassLoader(authenticator); + Thread.currentThread().setContextClassLoader(classLoader); + return; + } + } catch (IOException e) { + e.printStackTrace(); + } catch (AuthenticationException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + final GuiScreen errorGui = new GuiErrorScreen(null, null) { + + @Override + public void handleMouseInput() { + } + + @Override + public void handleKeyboardInput() { + } + + @Override + public void drawScreen(int par1, int par2, float par3) { + drawDefaultBackground(); + String[] s_msg = new String[] { + "Can't validate current installation of Dungeons Guide", + "Please contact mod author if you purchased this mod and getting this error", + "And if you haven't purchased the mod, please consider doing so" + }; + for (int i = 0; i < s_msg.length; ++i) { + drawCenteredString(fontRendererObj, s_msg[i], width / 2, height / 3 + 12 * i, 0xFFFFFFFF); + } + } + }; + @SuppressWarnings("serial") CustomModLoadingErrorDisplayException e = new CustomModLoadingErrorDisplayException() { + + @Override + public void initGui(GuiErrorScreen errorScreen, FontRenderer fontRenderer) { + Minecraft.getMinecraft().displayGuiScreen(errorGui); + } + + @Override + public void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int mouseRelX, int mouseRelY, float tickTime) { + } + }; + throw e; } public SkyblockStatus getSkyblockStatus() { diff --git a/src/main/java/kr/syeyoung/dungeonsguide/NetworkClassLoader.java b/src/main/java/kr/syeyoung/dungeonsguide/NetworkClassLoader.java new file mode 100644 index 00000000..564eeb11 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/NetworkClassLoader.java @@ -0,0 +1,57 @@ +package kr.syeyoung.dungeonsguide; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public class NetworkClassLoader extends ClassLoader { + Authenticator authenticator; + + public NetworkClassLoader(Authenticator authenticator) { + super(); + this.authenticator = authenticator; + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + byte[] b = new byte[0]; + try { + b = loadClassFromFile(name); + return defineClass(name, b, 0, b.length); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } + throw new ClassNotFoundException(); + } + + private byte[] loadClassFromFile(String fileName) throws BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException { + byte[] buffer; + InputStream inputStream = authenticator.getInputStream(fileName.replace('.', '/')+ ".class"); + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + int nextValue = 0; + while ( (nextValue = inputStream.read()) != -1 ) { + byteStream.write(nextValue); + } + buffer = byteStream.toByteArray(); + return buffer; + } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandLoadData.java b/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandLoadData.java index c1d3512e..637ec398 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandLoadData.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandLoadData.java @@ -5,6 +5,14 @@ import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoomInfoRegistry; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommandSender; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + public class CommandLoadData extends CommandBase { @Override public String getCommandName() { @@ -18,7 +26,23 @@ public class CommandLoadData extends CommandBase { @Override public void processCommand(ICommandSender sender, String[] args) { - DungeonRoomInfoRegistry.loadAll(DungeonsGuide.getDungeonsGuide().getConfigDir()); + try { + DungeonRoomInfoRegistry.loadAll(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } } @Override public int getRequiredPermissionLevel() { diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoomInfoRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoomInfoRegistry.java index d9df6a8d..383aea08 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoomInfoRegistry.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoomInfoRegistry.java @@ -1,8 +1,17 @@ package kr.syeyoung.dungeonsguide.dungeon.roomfinder; +import jdk.internal.util.xml.impl.Input; +import kr.syeyoung.dungeonsguide.Authenticator; +import kr.syeyoung.dungeonsguide.DungeonsGuide; import kr.syeyoung.dungeonsguide.dungeon.data.DungeonRoomInfo; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import java.io.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.*; public class DungeonRoomInfoRegistry { @@ -41,6 +50,7 @@ public class DungeonRoomInfoRegistry { } public static void saveAll(File dir) { + if (!DungeonsGuide.DEBUG) return; dir.mkdirs(); for (DungeonRoomInfo dungeonRoomInfo : registered) { try { @@ -53,14 +63,19 @@ public class DungeonRoomInfoRegistry { } } - public static void loadAll(File dir) { + public static void loadAll() throws BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException { registered.clear(); shapeMap.clear(); uuidMap.clear(); - for (File f: dir.listFiles()) { - if (!f.isFile() || !f.getName().endsWith(".roomdata")) continue; + Authenticator authenticator = DungeonsGuide.getDungeonsGuide().getAuthenticator(); + InputStream inputStream = authenticator.getInputStream("roomdata/datas.txt"); + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + String name; + while ((name = br.readLine()) != null) { + System.out.println("Trying to load "+name); + if (name.endsWith(".roomdata")) continue; try { - FileInputStream fis = new FileInputStream(f); + InputStream fis = authenticator.getInputStream(name); ObjectInputStream ois = new ObjectInputStream(fis); DungeonRoomInfo dri = (DungeonRoomInfo) ois.readObject(); ois.close(); diff --git a/src/main/resources/roomdata/datas.txt b/src/main/resources/roomdata/datas.txt new file mode 100644 index 00000000..6880da63 --- /dev/null +++ b/src/main/resources/roomdata/datas.txt @@ -0,0 +1,11 @@ +roomdata/990f6e4c-f7cf-4d27-ae91-11219b85861f.roomdata +roomdata/5000be9d-3081-4a5e-8563-dd826705663a.roomdata +roomdata/9139cb1c-b6f3-4bac-92de-909b1eb73449.roomdata +roomdata/11982f7f-703e-4d98-9d27-4e07ba3fef71.roomdata +roomdata/a053f4fa-d6b2-4aef-ae3e-97c7eee0252e.roomdata +roomdata/c2ea0a41-d495-437f-86cc-235a71c49f22.roomdata +roomdata/cf6d49d3-4f1e-4ec9-836e-049573793ddd.roomdata +roomdata/cf44c95c-950e-49e0-aa4c-82c2b18d0acc.roomdata +roomdata/d3e61abf-4198-4520-a950-a03761a0eb6f.roomdata +roomdata/ffd5411b-6ff4-4f60-b387-72f00510ec50.roomdata +roomdata/b2dce4ed-2bda-4303-a4d7-3ebb914db318.roomdata
\ No newline at end of file |