diff options
| author | syeyoung <cyoung06@naver.com> | 2022-11-15 23:26:59 +0900 |
|---|---|---|
| committer | syeyoung <cyoung06@naver.com> | 2022-11-15 23:52:55 +0900 |
| commit | 2893b632b4239dfb116dce853d07369a9fea222d (patch) | |
| tree | 2cd5a10264927d861d7d4573b76fb9181078beaa /loader/src/main | |
| parent | 0b4ce8c26a3126530599786d4a31c4bae44f7ec6 (diff) | |
| download | Skyblock-Dungeons-Guide-2893b632b4239dfb116dce853d07369a9fea222d.tar.gz Skyblock-Dungeons-Guide-2893b632b4239dfb116dce853d07369a9fea222d.tar.bz2 Skyblock-Dungeons-Guide-2893b632b4239dfb116dce853d07369a9fea222d.zip | |
- Authentication Overhaul to work with new dg 4.0 backend
Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'loader/src/main')
25 files changed, 521 insertions, 533 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java index 921adca9..efba137d 100755 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java @@ -20,9 +20,8 @@ package kr.syeyoung.dungeonsguide.launcher; import com.mojang.authlib.exceptions.AuthenticationUnavailableException; import com.mojang.authlib.exceptions.InvalidCredentialsException; +import kr.syeyoung.dungeonsguide.launcher.auth.AuthManager; import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthServerException; -import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator; -import kr.syeyoung.dungeonsguide.launcher.branch.ModDownloader; import kr.syeyoung.dungeonsguide.launcher.exceptions.NoSuitableLoaderFoundException; import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException; import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; @@ -64,10 +63,8 @@ public class Main private static File configDir; private DGInterface dgInterface; - private Authenticator authenticator = new Authenticator(); - private ModDownloader modDownloader = new ModDownloader(authenticator); - private List<DungeonsGuideReloadListener> listeners = new ArrayList<>(); + private final List<DungeonsGuideReloadListener> listeners = new ArrayList<>(); public static File getConfigDir() { return configDir; @@ -221,20 +218,17 @@ public class Main configDir = preInitializationEvent.getModConfigurationDirectory(); // setup preinit progress bar for well, progress bar! - ProgressManager.ProgressBar bar = ProgressManager.push("DungeonsGuide", 2); + ProgressManager.ProgressBar bar = ProgressManager.push("DungeonsGuide", 1); try { // Try authenticate bar.step("Authenticating..."); - authenticator.repeatAuthenticate(5); + AuthManager.getInstance().init(); // If authentication succeeds, obtain loader and partially load dungeons guide + File f = new File(preInitializationEvent.getModConfigurationDirectory(), "loader.cfg"); Configuration configuration = new Configuration(f); - - bar.step("Instantiating..."); - partialLoad(obtainLoader(configuration)); - // Save config because... well to generate it configuration.save(); } catch (Throwable t) { diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java index 23d9cf06..5aa6962b 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java @@ -4,23 +4,24 @@ import com.google.common.base.Throwables; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gson.JsonObject; import com.mojang.authlib.exceptions.AuthenticationException; -import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.AuthProvider; -import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth.DgAuth; -import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth.DgAuthUtil; -import kr.syeyoung.dungeonsguide.mod.chat.ChatTransmitter; -import kr.syeyoung.dungeonsguide.mod.events.impl.AuthChangedEvent; -import kr.syeyoung.dungeonsguide.mod.stomp.StompManager; +import kr.syeyoung.dungeonsguide.launcher.auth.token.*; +import kr.syeyoung.dungeonsguide.launcher.events.AuthChangedEvent; +import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthFailedExeption; +import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException; import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import java.io.IOException; +import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.util.Objects; import java.util.concurrent.*; @@ -40,13 +41,12 @@ public class AuthManager { private AuthToken currentToken = new NullToken(); - public String getToken() { - return currentToken.getToken(); + public AuthToken getToken() { + return currentToken; } - - - public KeyPair getKeyPair(){ - return currentToken.getRSAKeyForAuth(); + public String getWorkingTokenOrNull() { + if (currentToken instanceof DGAuthToken) return currentToken.getToken(); + else return null; } @@ -58,83 +58,80 @@ public class AuthManager { return; } - reauth(); initlock = true; - - MinecraftForge.EVENT_BUS.register(this); - ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("DgAuth Pool").build(); final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, namedThreadFactory); scheduler.scheduleAtFixedRate(() -> { - if (getToken() != null) { - JsonObject obj = DgAuthUtil.getJwtPayload(getToken()); - if (!obj.get("uuid").getAsString().replace("-", "").equals(Minecraft.getMinecraft().getSession().getPlayerID())) { - shouldReAuth = true; - } + boolean shouldReAuth = false; + if (!getToken().getUID().replace("-", "").equals(Minecraft.getMinecraft().getSession().getPlayerID())) { + shouldReAuth = true; } - - + if (!getToken().isAuthenticated()) { + shouldReAuth = true; + } + if (shouldReAuth) + reAuth(); }, 10,2000, TimeUnit.MILLISECONDS); - } - - boolean shouldReAuth = true; - int tickCounter; - - @SubscribeEvent - public void onTickClientTick(TickEvent.ClientTickEvent event) { - if (event.phase != TickEvent.Phase.START) return; - if (tickCounter % 200 == 0) { - tickCounter = 0; - reauth(); - } - tickCounter++; - } - public boolean isPlebUser(){ - return Objects.equals(getInstance().getPlanType(), "OPENSOURCE"); + reAuth(); } - public String getPlanType(){ - if(getToken() == null) return null; + private volatile boolean reauthLock = false; - JsonObject jwt = DgAuthUtil.getJwtPayload(getToken()); - if(!jwt.has("plan")) return null; + AuthToken reAuth() { + if (reauthLock) { + while(reauthLock); + return currentToken; + } - return jwt.get("plan").getAsString(); + reauthLock = true; + try { + String token = DgAuthUtil.requestAuth(baseserverurl); + byte[] encSecret = DgAuthUtil.checkSessionAuthenticityAndReturnEncryptedSecret(token); + currentToken = DgAuthUtil.verifyAuth(token, encSecret, baseserverurl); + MinecraftForge.EVENT_BUS.post(new AuthChangedEvent(currentToken)); + + if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException(); + } catch (NoSuchAlgorithmException | AuthenticationException | IOException | NoSuchPaddingException | + InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) { + currentToken = new FailedAuthToken(e); + // TODO: loader notifications on bottom right? +// ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true); + logger.error("Re-auth failed with message {}, trying again in a 2 seconds", String.valueOf(Throwables.getRootCause(e))); + throw new AuthFailedExeption(e); + } finally { + reauthLock = false; + } + return currentToken; } - void reauth() { - if (!shouldReAuth) return; - shouldReAuth = false; - currentToken = new NullToken(); - try { - currentProvider = new DgAuth(baseserverurl).createAuthProvider(); - if (currentProvider.getToken() == null) { - shouldReAuth = true; - currentProvider = null; - ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true); - logger.info("DG auth failed, trying again in a second"); - } else { - // RE-AUTHed SUCCESSFULLY HOORAY - // for some reason the forge events don't work in pre init, so I call the callback directly - StompManager.getInstance().init(); - MinecraftForge.EVENT_BUS.post(new AuthChangedEvent()); - } - } catch (NoSuchAlgorithmException | AuthenticationException | IOException e) { - shouldReAuth = true; - currentProvider = null; - ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true); - logger.error("Re-auth failed with message {}, trying again in a ten seconds", String.valueOf(Throwables.getRootCause(e))); + AuthToken acceptPrivacyPolicy() throws IOException { + if (reauthLock) { + while(reauthLock); + return currentToken; } + if (currentToken instanceof PrivacyPolicyRequiredToken) { + reauthLock = true; + try { + currentToken = DgAuthUtil.acceptNewPrivacyPolicy(currentToken.getToken(), baseserverurl); + if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException(); + } catch (IOException e) { + currentToken = new FailedAuthToken(e); + // TODO: loader notifications on bottom right? + logger.error("Accepting Privacy Policy failed with message {}, trying again in a 2 seconds", String.valueOf(Throwables.getRootCause(e))); + throw e; + } finally { + reauthLock = false; + } + } + return currentToken; } - - } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java index 9ce02643..e5408f0b 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java @@ -12,59 +12,16 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; public class AuthUtil { private AuthUtil() {} - public static KeyPair getKeyPair() throws NoSuchAlgorithmException { - KeyPairGenerator a = null; - a = KeyPairGenerator.getInstance("RSA"); - a.initialize(1024); - return a.generateKeyPair(); - } - - - public static JsonElement getJsonSecured(String u) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException{ - - int length = 0; - CipherInputStream cipherInputStream = null; - - HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL(u).openConnection(); - httpsURLConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); - httpsURLConnection.setRequestProperty("Content-Type", "application/json"); - httpsURLConnection.setRequestMethod("GET"); - httpsURLConnection.setRequestProperty("Authorization", AuthManager.getInstance().getToken()); - httpsURLConnection.setDoInput(true); - httpsURLConnection.setDoOutput(true); - - InputStream inputStream = httpsURLConnection.getInputStream(); - byte[] lengthPayload = new byte[4]; - inputStream.read(lengthPayload); - length = ((lengthPayload[0] & 0xFF) << 24) | - ((lengthPayload[1] & 0xFF) << 16) | - ((lengthPayload[2] & 0xFF) << 8) | - ((lengthPayload[3] & 0xFF)); - while (inputStream.available() < length) ; - byte[] keyPayload = new byte[length]; - inputStream.read(keyPayload); - - Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - cipher.init(Cipher.DECRYPT_MODE, AuthManager.getInstance().getKeyPair().getPrivate()); - byte[] AESKey = cipher.doFinal(keyPayload); - - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - SecretKeySpec secretKeySpec = new SecretKeySpec(AESKey, "AES"); - IvParameterSpec ivParameterSpec = new IvParameterSpec(AESKey); - cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); - cipherInputStream = new CipherInputStream(inputStream, cipher); - cipherInputStream.read(lengthPayload); - length = ((lengthPayload[0] & 0xFF) << 24) | - ((lengthPayload[1] & 0xFF) << 16) | - ((lengthPayload[2] & 0xFF) << 8) | - ((lengthPayload[3] & 0xFF)); - - httpsURLConnection.disconnect(); - - return new JsonParser().parse(new InputStreamReader(cipherInputStream)); + public static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { + PublicKey publicKey = + KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes)); + publicKey.getEncoded(); + return publicKey; } } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java new file mode 100644 index 00000000..5873fbda --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java @@ -0,0 +1,15 @@ +package kr.syeyoung.dungeonsguide.launcher.auth; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.json.JSONObject; + +@Data +@AllArgsConstructor +public class DGResponse<T> { + private final String status; + private final T data; + private final String errorMessage; + private final String qrCode; +} + diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java new file mode 100644 index 00000000..f585ac20 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java @@ -0,0 +1,160 @@ +package kr.syeyoung.dungeonsguide.launcher.auth; + +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 kr.syeyoung.dungeonsguide.launcher.auth.token.AuthToken; +import kr.syeyoung.dungeonsguide.launcher.auth.token.DGAuthToken; +import kr.syeyoung.dungeonsguide.launcher.auth.token.PrivacyPolicyRequiredToken; +import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthServerException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.ResponseParsingException; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Session; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.json.JSONObject; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.net.ssl.HttpsURLConnection; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.URL; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.util.stream.Collectors; + +public class DgAuthUtil { + private static final SecureRandom random = new SecureRandom(); + private DgAuthUtil(){} + + + private static <T> DGResponse<T> getResponse(HttpsURLConnection connection, Class<T> data) throws IOException { + connection.getResponseCode(); + InputStream toRead = connection.getErrorStream(); + if (toRead == null) + toRead = connection.getInputStream(); + String payload = IOUtils.readLines(toRead).stream().collect(Collectors.joining("\n")); + + try { + JSONObject json = new JSONObject(payload); + return new DGResponse( + json.getString("status"), + (T) json.get("data"), + json.getString("errorMessage"), + json.getString("qrCode") + ); + } catch (Exception e) { + throw new ResponseParsingException(payload, e.getMessage()); + } + + } + + public static String requestAuth(String baseurl) throws IOException { + GameProfile profile = Minecraft.getMinecraft().getSession().getProfile(); + + HttpsURLConnection connection = (HttpsURLConnection) new URL(baseurl + "/auth/requestAuth").openConnection(); + connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestMethod("POST"); + connection.setDoInput(true); + connection.setDoOutput(true); + + connection.getOutputStream().write(("{\"uuid\":\""+profile.getId().toString()+"\",\"nickname\":\""+profile.getName()+"\"}").getBytes()); + + DGResponse<String> preToken = getResponse(connection, String.class); + if (!"SUCCESS".equals(preToken.getStatus())) { + throw new AuthServerException(preToken); + } + + return preToken.getData(); + } + + public static byte[] checkSessionAuthenticityAndReturnEncryptedSecret(String tempToken) throws NoSuchAlgorithmException, AuthenticationException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + JSONObject d = getJwtPayload(tempToken); + byte[] sharedSecret = new byte[16]; + random.nextBytes(sharedSecret); + byte[] publicKey =Base64.decodeBase64(d.getString("publicKey")); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, AuthUtil.getPublicKey(publicKey)); + byte[] result = cipher.doFinal(sharedSecret); + + + String hash = calculateServerHash(sharedSecret, publicKey); + + Session session = Minecraft.getMinecraft().getSession(); + MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService(); + yggdrasilMinecraftSessionService.joinServer(session.getProfile(), session.getToken(), hash); + + return result; + } + + public static AuthToken verifyAuth(String tempToken, byte[] encSecret, String baseurl) throws IOException { + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(baseurl + "/auth/authenticate").openConnection(); + urlConnection.setRequestMethod("POST"); + urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setDoInput(true); + urlConnection.setDoOutput(true); + + urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"sharedSecret\":\""+Base64.encodeBase64URLSafeString(encSecret)+"\"}").getBytes()); + + DGResponse<JSONObject> postToken = getResponse(urlConnection, JSONObject.class); + if (!"SUCCESS".equals(postToken.getStatus())) { + throw new AuthServerException(postToken); + } + JSONObject data = postToken.getData(); + if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { + return new PrivacyPolicyRequiredToken(data.getString("jwt")); + } else if (data.getString("result").equals("SUCCESSFUL")) { + return new DGAuthToken(data.getString("jwt")); + } else { + throw new AuthServerException(postToken); + } + } + + public static AuthToken acceptNewPrivacyPolicy(String tempToken, String baseurl) throws IOException { + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(baseurl + "/auth/acceptPrivacyPolicy").openConnection(); + urlConnection.setRequestMethod("POST"); + urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setDoInput(true); + urlConnection.setDoOutput(true); + + urlConnection.getOutputStream().write(tempToken.getBytes()); + + DGResponse<JSONObject> postToken = getResponse(urlConnection, JSONObject.class); + if (!"SUCCESS".equals(postToken.getStatus())) { + throw new AuthServerException(postToken); + } + JSONObject data = postToken.getData(); + if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { + return new PrivacyPolicyRequiredToken(data.getString("jwt")); + } else if (data.getString("result").equals("SUCCESSFUL")) { + return new DGAuthToken(data.getString("jwt")); + } else { + throw new AuthServerException(postToken); + } + } + public static JSONObject getJwtPayload(String jwt) { + String midPart = jwt.split("\\.")[1].replace("+", "-").replace("/", "_"); + String base64Decode = new String(Base64.decodeBase64(midPart)); // padding + return new JSONObject(base64Decode); + } + + public static String calculateServerHash(byte[] a, byte[] b) throws NoSuchAlgorithmException { + MessageDigest c = MessageDigest.getInstance("SHA-1"); + c.update("".getBytes()); + c.update(a); + c.update(b); + byte[] d = c.digest(); + return new BigInteger(d).toString(16); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java deleted file mode 100644 index ebe78196..00000000 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java +++ /dev/null @@ -1,8 +0,0 @@ -package kr.syeyoung.dungeonsguide.launcher.auth; - -public class InvalidDungeonsGuideCredentialsException extends Throwable { - - public InvalidDungeonsGuideCredentialsException(String message) { - super(message); - } -} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java deleted file mode 100644 index 721b629f..00000000 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java +++ /dev/null @@ -1,164 +0,0 @@ -package kr.syeyoung.dungeonsguide.launcher.auth; - -import lombok.Setter; -import net.minecraftforge.common.MinecraftForge; -import org.apache.commons.codec.binary.Base64; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.HttpsURLConnection; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.security.*; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; -import java.util.HashMap; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class ResourceManager { - - Logger logger = LogManager.getLogger("ResourceManager"); - - @Setter - private String baseUrl; - @Setter - private String BASE64_X509ENCODEDKEYSPEC; - private final HashMap<String, byte[]> loadedResources = new HashMap<>(); - - - private static ResourceManager instance; - public static ResourceManager getInstance() { - if(instance == null) { - instance = new ResourceManager(); - MinecraftForge.EVENT_BUS.register(instance); - } - return instance; - } - - private ResourceManager() { - } - - public Map<String, byte[]> getResources() { - return loadedResources; - } - - - public void downloadAssets(String version) throws InvalidDungeonsGuideCredentialsException { - if(AuthManager.getInstance().getToken() == null) throw new InvalidDungeonsGuideCredentialsException("Not Authenticated while downloading assets"); - try { - // version not being null indicates that the user is "premium" - // so we download the special version - if (version != null) - downloadSafe( baseUrl + "/resource/version?v=" + version, true); - - if(!AuthManager.getInstance().isPlebUser()){ - downloadSafe(baseUrl + "/resource/roomdata", false); - } else { - logger.error("The current User is a pleb not downloading user data"); - } - - } catch (Exception t) { - t.printStackTrace(); - } - - } - - private void downloadSafe(String url, boolean isValidateSignature) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, SignatureException, InvalidKeySpecException { - HttpsURLConnection dgConnection = (HttpsURLConnection) new URL(url).openConnection(); - dgConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); - dgConnection.setRequestProperty("Content-Type", "application/json"); - dgConnection.setRequestMethod("GET"); - dgConnection.setRequestProperty("Authorization", AuthManager.getInstance().getToken()); - dgConnection.setDoInput(true); - dgConnection.setDoOutput(true); - - InputStream inputStream = dgConnection.getInputStream(); - byte[] lengthBytes = new byte[4]; - inputStream.read(lengthBytes); - int length = ((lengthBytes[0] & 0xFF) << 24) | - ((lengthBytes[1] & 0xFF) << 16) | - ((lengthBytes[2] & 0xFF) << 8) | - ((lengthBytes[3] & 0xFF)); - while (inputStream.available() < length) ; - byte[] keyPayload = new byte[length]; - inputStream.read(keyPayload); - - Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - cipher.init(Cipher.DECRYPT_MODE, AuthManager.getInstance().getKeyPair().getPrivate()); - byte[] h = cipher.doFinal(keyPayload); - - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - SecretKeySpec keySpec = new SecretKeySpec(h, "AES"); - IvParameterSpec ivSpec = new IvParameterSpec(h); - cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); - CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); - - cipherInputStream.read(lengthBytes); - length = ((lengthBytes[0] & 0xFF) << 24) | - ((lengthBytes[1 |
