Skip to content

Commit

Permalink
feat: Extend signing API
Browse files Browse the repository at this point in the history
This commit allows setting the keystore as well as the keystore entry password, alias and signer.

BREAKING CHANGE: This changes many signatures of existing APIs and adds new functions for signing
  • Loading branch information
oSumAtrIX committed Sep 23, 2023
1 parent 8da0c2b commit 592dc1c
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import app.revanced.lib.ApkUtils
import app.revanced.lib.Options
import app.revanced.lib.Options.setOptions
import app.revanced.lib.adb.AdbManager
import app.revanced.lib.signing.SigningOptions
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import app.revanced.patcher.Patcher
Expand Down Expand Up @@ -80,22 +79,34 @@ internal object PatchCommand : Runnable {
private var mount: Boolean = false

@CommandLine.Option(
names = ["--common-name"],
description = ["The common name of the signer of the patched APK file"],
showDefaultValue = ALWAYS
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"],
)
private var keystoreFilePath: File? = null

// key store password
@CommandLine.Option(
names = ["--keystore-password"],
description = ["The password of the keystore to sign the patched APK file with"],
)
private var commonName = "ReVanced"
private var keyStorePassword: String? = null // Empty password by default

@CommandLine.Option(
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
names = ["--alias"], description = ["The alias of the key from the keystore to sign the patched APK file with"],
showDefaultValue = ALWAYS
)
private var keystoreFilePath: File? = null
private var alias = "ReVanced Key"

@CommandLine.Option(
names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
names = ["--keystore-entry-password"],
description = ["The password of the entry from the keystore for the key to sign the patched APK file with"]
)
private var password = "ReVanced"
private var password = "" // Empty password by default

@CommandLine.Option(
names = ["--signer"], description = ["The name of the signer to sign the patched APK file with"],
showDefaultValue = ALWAYS
)
private var signer = "ReVanced"

@CommandLine.Option(
names = ["-r", "--resource-cache"],
Expand Down Expand Up @@ -208,16 +219,22 @@ internal object PatchCommand : Runnable {

// region Save

val tempFile = resourceCachePath.resolve(apk.name)
ApkUtils.copyAligned(apk, tempFile, patcherResult)
val tempFile = resourceCachePath.resolve(apk.name).apply {
ApkUtils.copyAligned(apk, this, patcherResult)
}

val keystoreFilePath = keystoreFilePath ?: outputFilePath.absoluteFile.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore")

if (!mount) ApkUtils.sign(
tempFile,
outputFilePath,
SigningOptions(
commonName,
ApkUtils.SigningOptions(
keystoreFilePath,
keyStorePassword,
alias,
password,
keystoreFilePath ?: outputFilePath.absoluteFile.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore"),
signer
)
)

Expand Down
51 changes: 34 additions & 17 deletions revanced-lib/api/revanced-lib.api
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
public final class app/revanced/lib/ApkUtils {
public static final field INSTANCE Lapp/revanced/lib/ApkUtils;
public final fun copyAligned (Ljava/io/File;Ljava/io/File;Lapp/revanced/patcher/PatcherResult;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/lib/signing/SigningOptions;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/lib/ApkUtils$SigningOptions;)V
}

public final class app/revanced/lib/ApkUtils$SigningOptions {
public fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getKeyStore ()Ljava/io/File;
public final fun getKeyStorePassword ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getSigner ()Ljava/lang/String;
}

public final class app/revanced/lib/Options {
Expand Down Expand Up @@ -77,23 +87,30 @@ public final class app/revanced/lib/logging/Logger {
}

public final class app/revanced/lib/signing/ApkSigner {
public fun <init> (Lapp/revanced/lib/signing/SigningOptions;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;)V
}

public final class app/revanced/lib/signing/SigningOptions {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/io/File;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Lapp/revanced/lib/signing/SigningOptions;
public static synthetic fun copy$default (Lapp/revanced/lib/signing/SigningOptions;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;ILjava/lang/Object;)Lapp/revanced/lib/signing/SigningOptions;
public fun equals (Ljava/lang/Object;)Z
public final fun getCommonName ()Ljava/lang/String;
public final fun getKeyStoreOutputFilePath ()Ljava/io/File;
public static final field INSTANCE Lapp/revanced/lib/signing/ApkSigner;
public final fun newApkSignerBuilder (Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
public final fun newApkSignerBuilder (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
public final fun newKeyStore (Ljava/util/List;)Ljava/security/KeyStore;
public final fun newKeystore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/List;)V
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/lib/signing/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
public final fun signApk (Lcom/android/apksig/ApkSigner$Builder;Ljava/io/File;Ljava/io/File;)V
}

public final class app/revanced/lib/signing/ApkSigner$KeyStoreEntry {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
}

public final class app/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair {
public fun <init> (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V
public final fun getCertificate ()Ljava/security/cert/X509Certificate;
public final fun getPrivateKey ()Ljava/security/PrivateKey;
}

public final class app/revanced/lib/zip/ZipFile : java/io/Closeable {
Expand Down
45 changes: 40 additions & 5 deletions revanced-lib/src/main/kotlin/app/revanced/lib/ApkUtils.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package app.revanced.lib

import app.revanced.lib.signing.ApkSigner
import app.revanced.lib.signing.SigningOptions
import app.revanced.lib.signing.ApkSigner.signApk
import app.revanced.lib.zip.ZipFile
import app.revanced.lib.zip.structures.ZipEntry
import app.revanced.patcher.PatcherResult
Expand Down Expand Up @@ -47,9 +47,8 @@ object ApkUtils {
}
}


/**
* Signs the apk at [apk] and writes it to [output].
* Signs the [apk] file and writes it to [output].
*
* @param apk The apk to sign.
* @param output The apk to write the signed apk to.
Expand All @@ -60,8 +59,44 @@ object ApkUtils {
output: File,
signingOptions: SigningOptions,
) {
logger.info("Signing ${apk.name}")
// Get the keystore from the file or create a new one.
val keyStore = if (signingOptions.keyStore.exists()) {
ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword)
} else {
val entry = ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password)

// Create a new keystore with a new keypair and saves it.
ApkSigner.newKeyStore(listOf(entry)).also { keyStore ->
keyStore.store(
signingOptions.keyStore.outputStream(),
signingOptions.keyStorePassword?.toCharArray()
)
}
}

ApkSigner(signingOptions).signApk(apk, output)
ApkSigner.newApkSignerBuilder(
keyStore,
signingOptions.alias,
signingOptions.password,
signingOptions.signer,
signingOptions.signer
).signApk(apk, output)
}

/**
* Options for signing an apk.
*
* @param keyStore The keystore to use for signing.
* @param keyStorePassword The password for the keystore.
* @param alias The alias of the key store entry to use for signing.
* @param password The password for recovering the signing key.
* @param signer The name of the signer.
*/
class SigningOptions(
val keyStore: File,
val keyStorePassword: String?,
val alias: String = "ReVanced Key",
val password: String = "",
val signer: String = "ReVanced",
)
}
Loading

0 comments on commit 592dc1c

Please sign in to comment.