From 11ef4b0227c4ac65879a2c714834d23008f2acd9 Mon Sep 17 00:00:00 2001 From: Graham Date: Wed, 15 Jan 2020 21:12:18 +0000 Subject: [PATCH] Sign loaders --- .../java/dev/openrs2/asm/classpath/Library.kt | 14 ++++ .../main/java/dev/openrs2/bundler/Bundler.kt | 13 ++-- .../openrs2/common/crypto/Pkcs12KeyStore.kt | 64 +++++++++++++++++++ conf/.gitignore | 1 + 4 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 common/src/main/java/dev/openrs2/common/crypto/Pkcs12KeyStore.kt diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt index f73846b2..d5000b91 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt +++ b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt @@ -3,6 +3,7 @@ package dev.openrs2.asm.classpath import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.hasCode import dev.openrs2.asm.remap.ClassForNameRemapper +import dev.openrs2.common.crypto.Pkcs12KeyStore import dev.openrs2.common.io.DeterministicJarOutputStream import dev.openrs2.common.io.SkipOutputStream import org.objectweb.asm.ClassReader @@ -101,6 +102,19 @@ class Library constructor() : Iterable { } } + public fun writeSignedJar(path: Path, keyStore: Pkcs12KeyStore, manifest: Manifest? = null) { + logger.info { "Writing signed jar $path" } + + val unsignedPath = Files.createTempFile("tmp", ".jar") + try { + writeJar(unsignedPath, manifest) + keyStore.signJar(unsignedPath) + DeterministicJarOutputStream.repack(unsignedPath, path) + } finally { + Files.deleteIfExists(unsignedPath) + } + } + fun writePack(out: OutputStream) { val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) try { diff --git a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt index 7140a8b8..7ed2da65 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt @@ -13,6 +13,7 @@ import dev.openrs2.bundler.transform.PlatformDetectionTransformer import dev.openrs2.bundler.transform.PublicKeyTransformer import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.bundler.transform.RightClickTransformer +import dev.openrs2.common.crypto.Pkcs12KeyStore import java.nio.file.Path import java.nio.file.Paths import java.util.jar.Attributes @@ -24,14 +25,14 @@ import javax.inject.Singleton fun main() { val injector = Guice.createInjector(BundlerModule()) val bundler = injector.getInstance(Bundler::class.java) - bundler.run(Paths.get("nonfree/code"), Paths.get("nonfree/code/bundle")) + bundler.run(Paths.get("nonfree/code"), Paths.get("nonfree/code/bundle"), Paths.get("conf/loader.p12")) } @Singleton class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { private val transformers = listOf(*TRANSFORMERS.toTypedArray(), publicKeyTransformer) - fun run(input: Path, output: Path) { + fun run(input: Path, output: Path, keyStorePath: Path) { // read input jars/packs logger.info { "Reading input jars" } val unpacker = Library.readJar(input.resolve("game_unpacker.dat")) @@ -116,12 +117,10 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { // write unsigned client and loaders client.writeJar(output.resolve("runescape.jar"), unsignedManifest) - loader.writeJar(output.resolve("loader.jar"), signedManifest) - glLoader.writeJar(output.resolve("loader_gl.jar"), signedManifest) - // sign loaders - logger.info { "Signing loaders" } - // TODO(gpe): implement + val keyStore = Pkcs12KeyStore.open(keyStorePath) + loader.writeSignedJar(output.resolve("loader.jar"), keyStore, signedManifest) + glLoader.writeSignedJar(output.resolve("loader_gl.jar"), keyStore, signedManifest) } companion object { diff --git a/common/src/main/java/dev/openrs2/common/crypto/Pkcs12KeyStore.kt b/common/src/main/java/dev/openrs2/common/crypto/Pkcs12KeyStore.kt new file mode 100644 index 00000000..b68dfc41 --- /dev/null +++ b/common/src/main/java/dev/openrs2/common/crypto/Pkcs12KeyStore.kt @@ -0,0 +1,64 @@ +package dev.openrs2.common.crypto + +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path + +class Pkcs12KeyStore private constructor(private val path: Path) { + fun signJar(jar: Path) { + exec( + "jarsigner", + "-keystore", path.toString(), + "-storetype", "pkcs12", + "-storepass", STORE_PASSWORD, + "-keypass", KEY_PASSWORD, + jar.toString(), + ALIAS + ) + } + + companion object { + private const val ALIAS = "openrs2" + private const val STORE_PASSWORD = ALIAS + private const val KEY_PASSWORD = ALIAS + private const val DNAME = "CN=OpenRS2" + private const val VALIDITY_PERIOD = "3650" + private const val KEY_ALGORITHM = "RSA" + private const val KEY_SIZE = "2048" + private const val SIGNATURE_ALGORITHM = "SHA256with$KEY_ALGORITHM" + + fun open(path: Path): Pkcs12KeyStore { + if (!Files.exists(path)) { + create(path) + } + return Pkcs12KeyStore(path) + } + + private fun create(path: Path) { + exec( + "keytool", + "-genkeypair", + "-keystore", path.toString(), + "-storetype", "pkcs12", + "-storepass", STORE_PASSWORD, + "-keypass", KEY_PASSWORD, + "-alias", ALIAS, + "-dname", DNAME, + "-validity", VALIDITY_PERIOD, + "-keyalg", KEY_ALGORITHM, + "-keysize", KEY_SIZE, + "-sigalg", SIGNATURE_ALGORITHM + ) + } + + private fun exec(command: String, vararg args: String) { + val commandWithArgs = listOf(command, *args) + val status = ProcessBuilder(commandWithArgs) + .start() + .waitFor() + if (status != 0) { + throw IOException("$command returned non-zero status code $status") + } + } + } +} diff --git a/conf/.gitignore b/conf/.gitignore index 5280d6cc..2fb49ccb 100644 --- a/conf/.gitignore +++ b/conf/.gitignore @@ -1,2 +1,3 @@ +/loader.p12 /private.key /public.key