diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..149cb3c --- /dev/null +++ b/.classpath @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..692ff10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/* +!src/ +!.settings/ +!.gitignore +!.project +!.classpath +!pom.xml diff --git a/.project b/.project new file mode 100644 index 0000000..809bdfa --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + MaiCrypt + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..672496e --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..80f31b6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + MaiCrypt + MaiCrypt + 0.0.1-SNAPSHOT + + src + + + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.3 + + + + me.despawningbone.maicrypt.Main + + + + + + package + + shade + + + + + + + + + org.apache.commons + commons-compress + 1.18 + + + \ No newline at end of file diff --git a/src/me/despawningbone/maicrypt/Main.java b/src/me/despawningbone/maicrypt/Main.java new file mode 100644 index 0000000..6106637 --- /dev/null +++ b/src/me/despawningbone/maicrypt/Main.java @@ -0,0 +1,123 @@ +package me.despawningbone.maicrypt; + +import java.awt.GraphicsEnvironment; +import java.io.Console; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; + +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.compress.compressors.gzip.GzipParameters; +import org.apache.commons.compress.utils.IOUtils; + +public class Main { + + public static void main(String[] args) throws IOException, InterruptedException { + Console console = System.console(); + if(console == null && !GraphicsEnvironment.isHeadless()){ + String filename = Main.class.getProtectionDomain().getCodeSource().getLocation().toString().substring(6); + if(System.getProperty("os.name").contains("Win")) Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "cmd", "/c", "java -jar \"" + filename + "\""}); + }else{ + System.out.println("-= maimai .bin data file decryption tool =-\n"); + Boolean encrypt = null; + while (encrypt == null) { + System.out.print("Encrypt/decrypt (e/d)? "); + String line = System.console().readLine(); + if (line.equalsIgnoreCase("e") || line.equalsIgnoreCase("d")) + encrypt = line.equalsIgnoreCase("e"); + } + System.out.println("Selected " + (encrypt ? "encrypt" : "decrypt") + " mode."); + System.out.println("Please put the files in the same directory as the jar now (including a \"Keyfile.txt\" with only the key in hex form)."); + String key = Files.readAllLines(new File("Keyfile.txt").toPath()).get(0).replaceAll("\\s+",""); + String base, out; + if(encrypt) { + System.out.print("Output file name (the input file name should be the same as output but without file extension): "); + out = System.console().readLine(); + base = out.substring(0, out.lastIndexOf(".")); + compressGzip(base, base + "_EDITED.gz"); + processFile(true, new File(base + "_EDITED.gz"), DatatypeConverter.parseHexBinary(key), new File(out)); + } else { + System.out.print("Input file name: "); + String in = System.console().readLine(); + base = in.substring(0, in.lastIndexOf(".")); + processFile(false, new File(in), DatatypeConverter.parseHexBinary(key), + new File(base + ".gz")); + decompressGzip(base + ".gz", base); + out = base; + } + System.out.print("Done. Check the directory for the " + base + (encrypt ? "_EDITED" : "") + ".gz archive and the " + out + " file!\nPress any key to exit..."); + System.console().readLine(); + } + } + + private static void processFile(boolean encrypt, File input, byte[] byteKey, File output) { + try (FileInputStream fileInputStream = new FileInputStream(input); + FileOutputStream fileOutputStream = new FileOutputStream(output)) { + Key key = new SecretKeySpec(byteKey, "AES"); + + byte[] inputBytes = new byte[(int) input.length()]; + fileInputStream.read(inputBytes); + if(encrypt) { //append 16 bytes before encrypting + ByteBuffer buf = ByteBuffer.allocate(inputBytes.length + 16); + buf.put(new byte[16]); + buf.put(inputBytes); + inputBytes = buf.array(); + } + + byte[] iv = Arrays.copyOfRange(inputBytes, 0, 16); + IvParameterSpec ivspec = new IvParameterSpec(iv); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key, ivspec); + + byte[] outputBytes = cipher.doFinal(inputBytes); + + if(!encrypt) outputBytes = Arrays.copyOfRange(outputBytes, 16, outputBytes.length); + + fileOutputStream.write(outputBytes); + + fileInputStream.close(); + fileOutputStream.close(); + + } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + e.printStackTrace(); + } + } + + private static void compressGzip(String file, String gzipFile) { + GzipParameters param = new GzipParameters(); + param.setFilename(file); + try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(new FileOutputStream(gzipFile), param); FileInputStream fi = new FileInputStream(file)) { + IOUtils.copy(fi, out); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void decompressGzip(String gzipFile, String file) { + try (GzipCompressorInputStream in = new GzipCompressorInputStream(new FileInputStream(gzipFile)); FileOutputStream fo = new FileOutputStream(file)) { + IOUtils.copy(in, fo); + } catch (IOException e) { + e.printStackTrace(); + } + } +}