aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/kr/syeyoung/dungeonsguide/auth
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/kr/syeyoung/dungeonsguide/auth')
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/AuthManager.java150
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/AuthUtil.java70
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/InvalidDungeonsGuideCredentialsException.java8
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/ResourceManager.java164
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/AuthProvider.java16
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuth.java46
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuthUtil.java88
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/NullAuth.java62
8 files changed, 604 insertions, 0 deletions
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthManager.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthManager.java
new file mode 100644
index 00000000..5588a4a2
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthManager.java
@@ -0,0 +1,150 @@
+package kr.syeyoung.dungeonsguide.auth;
+
+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.auth.authprovider.AuthProvider;
+import kr.syeyoung.dungeonsguide.auth.authprovider.DgAuth.DgAuth;
+import kr.syeyoung.dungeonsguide.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 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 java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+
+public class AuthManager {
+ Logger logger = LogManager.getLogger("AuthManger");
+
+ private static AuthManager instance;
+
+ public static AuthManager getInstance() {
+ if(instance == null) instance = new AuthManager();
+ return instance;
+ }
+
+
+ @Setter
+ private String baseserverurl = "https://dungeons.guide";
+
+ private AuthProvider currentProvider;
+
+ public String getToken() {
+ if (currentProvider != null && currentProvider.getToken() != null) {
+ return currentProvider.getToken();
+ }
+ return null;
+ }
+
+ public KeyPair getKeyPair(){
+ if (currentProvider != null && currentProvider.getToken() != null) {
+ return currentProvider.getRsaKey();
+ }
+ return null;
+ }
+
+
+ boolean initlock = false;
+
+ public void init() {
+ if (initlock) {
+ logger.info("Cannot init AuthManger twice");
+ 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;
+ }
+ }
+ }, 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");
+ }
+
+ public String getPlanType(){
+ if(getToken() == null) return null;
+
+
+ JsonObject jwt = DgAuthUtil.getJwtPayload(getToken());
+
+ if(!jwt.has("plan")) return null;
+
+ return jwt.get("plan").getAsString();
+
+ }
+
+ void reauth() {
+ if (!shouldReAuth) return;
+
+ shouldReAuth = false;
+
+ currentProvider = null;
+ 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)));
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthUtil.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthUtil.java
new file mode 100644
index 00000000..d96b054d
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/AuthUtil.java
@@ -0,0 +1,70 @@
+package kr.syeyoung.dungeonsguide.auth;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.HttpsURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.security.*;
+
+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));
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/InvalidDungeonsGuideCredentialsException.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/InvalidDungeonsGuideCredentialsException.java
new file mode 100644
index 00000000..98caa049
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/InvalidDungeonsGuideCredentialsException.java
@@ -0,0 +1,8 @@
+package kr.syeyoung.dungeonsguide.auth;
+
+public class InvalidDungeonsGuideCredentialsException extends Throwable {
+
+ public InvalidDungeonsGuideCredentialsException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/ResourceManager.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/ResourceManager.java
new file mode 100644
index 00000000..56d46aea
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/ResourceManager.java
@@ -0,0 +1,164 @@
+package kr.syeyoung.dungeonsguide.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] & 0xFF) << 16) |
+ ((lengthBytes[2] & 0xFF) << 8) |
+ ((lengthBytes[3] & 0xFF));
+
+ int totalLen = length;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ byte[] buff = new byte[256];
+ while (totalLen > 0) {
+ int len = cipherInputStream.read(buff, 0, Math.min(buff.length, totalLen));
+ totalLen -= len;
+ bos.write(buff, 0, len);
+ }
+ byte[] body = bos.toByteArray();
+
+ byte[] signed;
+ if (isValidateSignature) {
+ cipherInputStream.read(lengthBytes,0 , 4);
+ length = ((lengthBytes[0] & 0xFF) << 24) |
+ ((lengthBytes[1] & 0xFF) << 16) |
+ ((lengthBytes[2] & 0xFF) << 8) |
+ ((lengthBytes[3] & 0xFF));
+
+ totalLen = length;
+ bos = new ByteArrayOutputStream();
+ while (totalLen > 0) {
+ int len = cipherInputStream.read(buff, 0, Math.min(buff.length, totalLen));
+ totalLen -= len;
+ bos.write(buff, 0, len);
+ }
+ signed = bos.toByteArray();
+
+ Signature sign = Signature.getInstance("SHA512withRSA");
+ sign.initVerify(getPublicKey(BASE64_X509ENCODEDKEYSPEC));
+ sign.update(body);
+ boolean truth = sign.verify(signed);
+ if (!truth) throw new SignatureException("DG SIGNATURE FORGED");
+ }
+
+ ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(body));
+ ZipEntry zipEntry;
+ while ((zipEntry=zipInputStream.getNextEntry()) != null) {
+ byte[] buffer = new byte[256];
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ int p;
+ while((p = zipInputStream.read(buffer)) > 0) {
+ byteArrayOutputStream.write(buffer, 0, p);
+ }
+ this.loadedResources.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());
+ }
+ }
+
+
+ public static PublicKey getPublicKey(String base64X509EncodedKeySpec) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decodeBase64(base64X509EncodedKeySpec));
+
+ return KeyFactory.getInstance("RSA").generatePublic(spec);
+ }
+
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/AuthProvider.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/AuthProvider.java
new file mode 100644
index 00000000..ba42574e
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/AuthProvider.java
@@ -0,0 +1,16 @@
+package kr.syeyoung.dungeonsguide.auth.authprovider;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+
+public interface AuthProvider {
+ String getToken();
+
+ KeyPair getRsaKey();
+
+
+ AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuth.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuth.java
new file mode 100644
index 00000000..0e7a05f6
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuth.java
@@ -0,0 +1,46 @@
+package kr.syeyoung.dungeonsguide.auth.authprovider.DgAuth;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+import kr.syeyoung.dungeonsguide.auth.AuthUtil;
+import kr.syeyoung.dungeonsguide.auth.authprovider.AuthProvider;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+
+public class DgAuth implements AuthProvider {
+
+ private final String authServerUrl;
+
+ public DgAuth(String authServerUrl){
+ this.authServerUrl = authServerUrl;
+ }
+
+ private String token;
+ private KeyPair rsaKey;
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public KeyPair getRsaKey() {
+ return rsaKey;
+ }
+
+
+ @Override
+ public AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException {
+ this.rsaKey = AuthUtil.getKeyPair();
+
+ String tempToken = DgAuthUtil.requestAuth(this.authServerUrl);
+
+ DgAuthUtil.checkSessionAuthenticity(tempToken);
+
+ this.token = DgAuthUtil.verifyAuth(tempToken, rsaKey.getPublic(), authServerUrl);
+
+ return this;
+ }
+
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuthUtil.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuthUtil.java
new file mode 100644
index 00000000..b01fba50
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/DgAuth/DgAuthUtil.java
@@ -0,0 +1,88 @@
+package kr.syeyoung.dungeonsguide.auth.authprovider.DgAuth;
+
+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 net.minecraft.client.Minecraft;
+import net.minecraft.util.Session;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
+public class DgAuthUtil {
+ private DgAuthUtil(){}
+
+ 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());
+ String payload = String.join("\n", IOUtils.readLines(connection.getErrorStream() == null ? connection.getInputStream() : connection.getErrorStream()));
+
+ JsonObject json = (JsonObject) new JsonParser().parse(payload);
+
+ if (!"ok".equals(json.get("status").getAsString())) {
+ return null;
+ }
+ return json.get("data").getAsString();
+ }
+
+ public static void checkSessionAuthenticity(String tempToken) throws NoSuchAlgorithmException, AuthenticationException {
+ JsonObject d = getJwtPayload(tempToken);
+ byte[] sharedSecret = Base64.decodeBase64(d.get("sharedSecret").getAsString());
+ byte[] publicKey =Base64.decodeBase64(d.get("publicKey").getAsString());
+ String hash = calculateServerHash(sharedSecret, publicKey);
+
+ Session session = Minecraft.getMinecraft().getSession();
+ MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService();
+ yggdrasilMinecraftSessionService.joinServer(session.getProfile(), session.getToken(), hash);
+ }
+
+ public static String verifyAuth(String tempToken, PublicKey clientKey, 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+"\",\"publicKey\":\""+Base64.encodeBase64URLSafeString(clientKey.getEncoded())+"\"}").getBytes());
+ String payload = String.join("\n", IOUtils.readLines(urlConnection.getErrorStream() == null ? urlConnection.getInputStream() : urlConnection.getErrorStream()));
+
+ JsonObject jsonObject = (JsonObject) new JsonParser().parse(payload);
+ if (!"ok".equals(jsonObject.get("status").getAsString())) {
+ return null;
+ }
+ return jsonObject.get("data").getAsString();
+ }
+
+ public static JsonObject getJwtPayload(String jwt) {
+ String midPart = jwt.split("\\.")[1].replace("+", "-").replace("/", "_");
+ String base64Decode = new String(Base64.decodeBase64(midPart)); // padding
+ return (JsonObject) new JsonParser().parse(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/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/NullAuth.java b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/NullAuth.java
new file mode 100644
index 00000000..ec7e9aed
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/auth/authprovider/NullAuth.java
@@ -0,0 +1,62 @@
+package kr.syeyoung.dungeonsguide.auth.authprovider;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+public class NullAuth implements AuthProvider {
+
+ Logger logger = LogManager.getLogger("NullAuth");
+
+ @Override
+ public String getToken() {
+ return "TOKEN";
+ }
+
+ @Override
+ public KeyPair getRsaKey() {
+ return new KeyPair(new PublicKey() {
+ @Override
+ public String getAlgorithm() {
+ return null;
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return new byte[0];
+ }
+ }, new PrivateKey() {
+ @Override
+ public String getAlgorithm() {
+ return null;
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return new byte[0];
+ }
+ });
+ }
+
+ @Override
+ public AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException {
+ return new NullAuth();
+ }
+
+}