mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-11-19 01:59:25 +01:00
feat: Extend signing API
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
This commit is contained in:
parent
8da0c2bdfe
commit
592dc1c64a
@ -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
|
||||
@ -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
|
||||
|
||||
)
|
||||
private var commonName = "ReVanced"
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
|
||||
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 = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
|
||||
names = ["--keystore-password"],
|
||||
description = ["The password of the keystore to sign the patched APK file with"],
|
||||
)
|
||||
private var password = "ReVanced"
|
||||
private var keyStorePassword: String? = null // Empty password by default
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--alias"], description = ["The alias of the key from the keystore to sign the patched APK file with"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
private var alias = "ReVanced Key"
|
||||
|
||||
@CommandLine.Option(
|
||||
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 = "" // 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"],
|
||||
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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 {
|
||||
@ -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 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/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 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 {
|
||||
|
@ -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
|
||||
@ -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.
|
||||
@ -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)
|
||||
|
||||
ApkSigner(signingOptions).signApk(apk, output)
|
||||
// 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.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",
|
||||
)
|
||||
}
|
@ -6,85 +6,256 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.bouncycastle.operator.ContentSigner
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.math.BigInteger
|
||||
import java.security.*
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.logging.Logger
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
class ApkSigner(
|
||||
private val signingOptions: SigningOptions
|
||||
) {
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
object ApkSigner {
|
||||
private val logger = Logger.getLogger(app.revanced.lib.signing.ApkSigner::class.java.name)
|
||||
|
||||
private val signer: ApkSigner.Builder
|
||||
private val passwordCharArray = signingOptions.password.toCharArray()
|
||||
|
||||
init {
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
|
||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
||||
val alias = keyStore.let { store ->
|
||||
FileInputStream(signingOptions.keyStoreOutputFilePath.also {
|
||||
if (!it.exists()) {
|
||||
logger.info("Creating keystore at ${it.absolutePath}")
|
||||
newKeystore(it)
|
||||
} else {
|
||||
logger.info("Using keystore ${it.absolutePath}")
|
||||
}
|
||||
}).use { fis -> store.load(fis, null) }
|
||||
store.aliases().nextElement()
|
||||
}
|
||||
|
||||
with(
|
||||
ApkSigner.SignerConfig.Builder(
|
||||
signingOptions.commonName,
|
||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
||||
).build()
|
||||
) {
|
||||
this@ApkSigner.signer = ApkSigner.Builder(listOf(this))
|
||||
signer.setCreatedBy(signingOptions.commonName)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a new [PrivateKeyCertificatePair].
|
||||
*
|
||||
* @param commonName The common name of the certificate.
|
||||
* @param validUntil The date until the certificate is valid.
|
||||
* @return The created [PrivateKeyCertificatePair].
|
||||
*/
|
||||
fun newPrivateKeyCertificatePair(
|
||||
commonName: String = "ReVanced",
|
||||
validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24)
|
||||
): PrivateKeyCertificatePair {
|
||||
logger.fine("Creating certificate for $commonName")
|
||||
|
||||
private fun newKeystore(out: File) {
|
||||
val (publicKey, privateKey) = createKey()
|
||||
val privateKS = KeyStore.getInstance("BKS", "BC")
|
||||
privateKS.load(null, passwordCharArray)
|
||||
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
|
||||
privateKS.store(FileOutputStream(out), passwordCharArray)
|
||||
}
|
||||
// Generate a new key pair.
|
||||
val keyPair = KeyPairGenerator.getInstance("RSA").apply {
|
||||
initialize(2048)
|
||||
}.generateKeyPair()
|
||||
|
||||
private fun createKey(): Pair<X509Certificate, PrivateKey> {
|
||||
val gen = KeyPairGenerator.getInstance("RSA")
|
||||
gen.initialize(2048)
|
||||
val pair = gen.generateKeyPair()
|
||||
var serialNumber: BigInteger
|
||||
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
||||
val x500Name = X500Name("CN=${signingOptions.commonName}")
|
||||
val builder = X509v3CertificateBuilder(
|
||||
x500Name,
|
||||
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong())
|
||||
while (serialNumber < BigInteger.ZERO)
|
||||
|
||||
val name = X500Name("CN=$commonName")
|
||||
|
||||
// Create a new certificate.
|
||||
val certificate = JcaX509CertificateConverter().getCertificate(
|
||||
X509v3CertificateBuilder(
|
||||
name,
|
||||
serialNumber,
|
||||
Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L),
|
||||
Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 366L * 30L),
|
||||
Date(System.currentTimeMillis()),
|
||||
validUntil,
|
||||
Locale.ENGLISH,
|
||||
x500Name,
|
||||
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
|
||||
name,
|
||||
SubjectPublicKeyInfo.getInstance(keyPair.public.encoded)
|
||||
).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private))
|
||||
)
|
||||
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
|
||||
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
|
||||
|
||||
return PrivateKeyCertificatePair(keyPair.private, certificate)
|
||||
}
|
||||
|
||||
fun signApk(input: File, output: File) {
|
||||
signer.setInputApk(input)
|
||||
signer.setOutputApk(output)
|
||||
/**
|
||||
* Create a new keystore with a new keypair.
|
||||
*
|
||||
* @param entries The entries to add to the keystore.
|
||||
* @return The created keystore.
|
||||
* @see KeyStoreEntry
|
||||
*/
|
||||
fun newKeyStore(
|
||||
entries: List<KeyStoreEntry>
|
||||
): KeyStore {
|
||||
logger.fine("Creating keystore")
|
||||
|
||||
signer.build().sign()
|
||||
return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply {
|
||||
entries.forEach { entry ->
|
||||
load(null)
|
||||
// Add all entries to the keystore.
|
||||
setKeyEntry(
|
||||
entry.alias,
|
||||
entry.privateKeyCertificatePair.privateKey,
|
||||
entry.password.toCharArray(),
|
||||
arrayOf(entry.privateKeyCertificatePair.certificate)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new keystore with a new keypair and saves it to the given [keyStoreOutputStream].
|
||||
*
|
||||
* @param keyStoreOutputStream The stream to write the keystore to.
|
||||
* @param keyStorePassword The password for the keystore.
|
||||
* @param entries The entries to add to the keystore.
|
||||
*/
|
||||
fun newKeystore(
|
||||
keyStoreOutputStream: OutputStream,
|
||||
keyStorePassword: String,
|
||||
entries: List<KeyStoreEntry>
|
||||
) = newKeyStore(entries).store(
|
||||
keyStoreOutputStream,
|
||||
keyStorePassword.toCharArray()
|
||||
) // Save the keystore.
|
||||
|
||||
/**
|
||||
* Read a keystore from the given [keyStoreInputStream].
|
||||
*
|
||||
* @param keyStoreInputStream The stream to read the keystore from.
|
||||
* @param keyStorePassword The password for the keystore.
|
||||
* @return The keystore.
|
||||
* @throws IllegalArgumentException If the keystore password is invalid.
|
||||
*/
|
||||
fun readKeyStore(
|
||||
keyStoreInputStream: InputStream,
|
||||
keyStorePassword: String?
|
||||
): KeyStore {
|
||||
logger.fine("Reading keystore")
|
||||
|
||||
return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply {
|
||||
try {
|
||||
load(keyStoreInputStream, keyStorePassword?.toCharArray())
|
||||
} catch (exception: IOException) {
|
||||
if (exception.cause is UnrecoverableKeyException)
|
||||
throw IllegalArgumentException("Invalid keystore password")
|
||||
else
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [ApkSigner.Builder].
|
||||
*
|
||||
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
|
||||
* @param signer The name of the signer.
|
||||
* @param createdBy The value for the `Created-By` attribute in the APK's manifest.
|
||||
* @return The created [ApkSigner.Builder] instance.
|
||||
*/
|
||||
fun newApkSignerBuilder(
|
||||
privateKeyCertificatePair: PrivateKeyCertificatePair,
|
||||
signer: String,
|
||||
createdBy: String
|
||||
): ApkSigner.Builder {
|
||||
logger.fine(
|
||||
"Creating new ApkSigner " +
|
||||
"with $signer as signer and " +
|
||||
"$createdBy as Created-By attribute in the APK's manifest"
|
||||
)
|
||||
|
||||
// Create the signer config.
|
||||
val signerConfig = ApkSigner.SignerConfig.Builder(
|
||||
signer,
|
||||
privateKeyCertificatePair.privateKey,
|
||||
listOf(privateKeyCertificatePair.certificate)
|
||||
).build()
|
||||
|
||||
// Create the signer.
|
||||
return ApkSigner.Builder(listOf(signerConfig)).apply {
|
||||
setCreatedBy(createdBy)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a [PrivateKeyCertificatePair] from a keystore entry.
|
||||
*
|
||||
* @param keyStore The keystore to read the entry from.
|
||||
* @param keyStoreEntryAlias The alias of the key store entry to read.
|
||||
* @param keyStoreEntryPassword The password for recovering the signing key.
|
||||
* @return The read [PrivateKeyCertificatePair].
|
||||
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
|
||||
*/
|
||||
fun readKeyCertificatePair(
|
||||
keyStore: KeyStore,
|
||||
keyStoreEntryAlias: String,
|
||||
keyStoreEntryPassword: String,
|
||||
): PrivateKeyCertificatePair {
|
||||
logger.fine("Reading key and certificate pair from keystore entry $keyStoreEntryAlias")
|
||||
|
||||
if (!keyStore.containsAlias(keyStoreEntryAlias))
|
||||
throw IllegalArgumentException("Keystore does not contain alias $keyStoreEntryAlias")
|
||||
|
||||
// Read the private key and certificate from the keystore.
|
||||
|
||||
val privateKey = try {
|
||||
keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey
|
||||
} catch (exception: UnrecoverableKeyException) {
|
||||
throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias")
|
||||
}
|
||||
|
||||
val certificate = keyStore.getCertificate(keyStoreEntryAlias) as X509Certificate
|
||||
|
||||
return PrivateKeyCertificatePair(privateKey, certificate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [ApkSigner.Builder].
|
||||
*
|
||||
* @param keyStore The keystore to use for signing.
|
||||
* @param keyStoreEntryAlias The alias of the key store entry to use for signing.
|
||||
* @param keyStoreEntryPassword The password for recovering the signing key.
|
||||
* @param signer The name of the signer.
|
||||
* @param createdBy The value for the `Created-By` attribute in the APK's manifest.
|
||||
* @return The created [ApkSigner.Builder] instance.
|
||||
* @see KeyStoreEntry
|
||||
* @see PrivateKeyCertificatePair
|
||||
* @see ApkSigner.Builder.setCreatedBy
|
||||
* @see ApkSigner.Builder.signApk
|
||||
*/
|
||||
fun newApkSignerBuilder(
|
||||
keyStore: KeyStore,
|
||||
keyStoreEntryAlias: String,
|
||||
keyStoreEntryPassword: String,
|
||||
signer: String,
|
||||
createdBy: String,
|
||||
) = newApkSignerBuilder(
|
||||
readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword),
|
||||
signer,
|
||||
createdBy
|
||||
)
|
||||
|
||||
fun ApkSigner.Builder.signApk(input: File, output: File) {
|
||||
logger.info("Signing ${input.name}")
|
||||
|
||||
setInputApk(input)
|
||||
setOutputApk(output)
|
||||
|
||||
build().sign()
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in a keystore.
|
||||
*
|
||||
* @param alias The alias of the entry.
|
||||
* @param password The password for recovering the signing key.
|
||||
* @param privateKeyCertificatePair The private key and certificate pair.
|
||||
* @see PrivateKeyCertificatePair
|
||||
*/
|
||||
class KeyStoreEntry(
|
||||
val alias: String,
|
||||
val password: String,
|
||||
val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair()
|
||||
)
|
||||
|
||||
/**
|
||||
* A private key and certificate pair.
|
||||
*
|
||||
* @param privateKey The private key.
|
||||
* @param certificate The certificate.
|
||||
*/
|
||||
class PrivateKeyCertificatePair(
|
||||
val privateKey: PrivateKey,
|
||||
val certificate: X509Certificate,
|
||||
)
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package app.revanced.lib.signing
|
||||
|
||||
import java.io.File
|
||||
|
||||
data class SigningOptions(
|
||||
val commonName: String,
|
||||
val password: String,
|
||||
val keyStoreOutputFilePath: File
|
||||
)
|
Loading…
Reference in New Issue
Block a user