fix: Sign APKs using apksig

Previously, the signing extension from apkzlib was used incorrectly. The extension is meant to be added to a ZFile whereas on changes the extension would be used to sign. Instead the extension was added to a newly created ZFile, and without any changes, closed again, leading to no APK being signed. It turns out to be impractical to use the signing extension as we do not write an entire APK ZFile so that the signing extension can consider every file inside the ZFile, instead we just merge the patcher result to an existing ZFile. Instead use `apksig` after applying the patcher result to the ZFile which signs everything correctly.
This commit is contained in:
oSumAtrIX
2024-03-09 00:56:15 +01:00
parent c92be32607
commit f59ecbccd1
5 changed files with 141 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
package app.revanced.library
import com.android.apksig.ApkSigner.SignerConfig
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zip.ZFile
@@ -182,6 +183,7 @@ object ApkSigner {
/**
* Create a new [Signer].
*
* @param signer The name of the signer.
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*
* @return The new [Signer].
@@ -189,6 +191,38 @@ object ApkSigner {
* @see PrivateKeyCertificatePair
* @see Signer
*/
fun newApkSigner(
signer: String,
privateKeyCertificatePair: PrivateKeyCertificatePair,
) = Signer(
com.android.apksig.ApkSigner.Builder(
listOf(
SignerConfig.Builder(
signer,
privateKeyCertificatePair.privateKey,
listOf(privateKeyCertificatePair.certificate),
).build(),
),
),
)
/**
* Create a new [Signer].
*
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*
* @return The new [Signer].
*
* @see PrivateKeyCertificatePair
* @see Signer
*/
@Suppress("DEPRECATION")
@Deprecated(
"This method will be removed in the future.",
ReplaceWith(
"newApkSigner(\"ReVanced\", privateKeyCertificatePair)",
),
)
fun newApkSigner(privateKeyCertificatePair: PrivateKeyCertificatePair) =
Signer(
SigningExtension(
@@ -205,6 +239,7 @@ object ApkSigner {
/**
* Create a new [Signer].
*
* @param signer The name of the signer.
* @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.
@@ -215,10 +250,36 @@ object ApkSigner {
* @see Signer
*/
fun newApkSigner(
signer: String,
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = newApkSigner(readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
) = newApkSigner(signer, readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
/**
* Create a new [Signer].
*
* @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.
*
* @return The new [Signer].
*
* @see KeyStore
* @see Signer
*/
@Deprecated(
"This method will be removed in the future.",
ReplaceWith(
"newApkSigner(\"ReVanced\", readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))",
"app.revanced.library.ApkSigner.newApkSigner",
),
)
fun newApkSigner(
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = newApkSigner("ReVanced", readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
/**
* An entry in a keystore.
@@ -246,23 +307,48 @@ object ApkSigner {
val certificate: X509Certificate,
)
class Signer internal constructor(private val signingExtension: SigningExtension) {
class Signer {
private val signerBuilder: com.android.apksig.ApkSigner.Builder?
private val signingExtension: SigningExtension?
internal constructor(signerBuilder: com.android.apksig.ApkSigner.Builder) {
this.signerBuilder = signerBuilder
signingExtension = null
}
@Deprecated("This constructor will be removed in the future.")
internal constructor(signingExtension: SigningExtension) {
signerBuilder = null
this.signingExtension = signingExtension
}
/**
* Sign an APK file.
*
* @param apkFile The APK file to sign.
*/
fun signApk(apkFile: File) = ZFile.openReadWrite(apkFile).use { signApk(it) }
@Deprecated("This method will be removed in the future.")
fun signApk(apkFile: File) = ZFile.openReadWrite(apkFile).use {
@Suppress("DEPRECATION")
signApk(it)
}
/**
* Sign an APK file.
*
* @param apkZFile The APK [ZFile] to sign.
*/
@Deprecated("This method will be removed in the future.")
fun signApk(apkZFile: ZFile) {
logger.info("Signing ${apkZFile.file.name}")
signingExtension.register(apkZFile)
signingExtension?.register(apkZFile)
}
fun signApk(inputApkFile: File, outputApkFile: File) {
logger.info("Signing APK")
signerBuilder?.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign()
}
}
}

View File

@@ -84,7 +84,7 @@ object ApkUtils {
}
}
logger.info("Aligning ${apkFile.name}")
logger.info("Aligning APK")
targetApkZFile.realign()
@@ -92,28 +92,38 @@ object ApkUtils {
}
}
/**
* Reads an existing or creates a new keystore.
*
* @param signingOptions The options to use for signing.
*/
private fun readOrNewKeyStore(signingOptions: SigningOptions) = 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(setOf(entry)).apply {
store(
signingOptions.keyStore.outputStream(),
signingOptions.keyStorePassword?.toCharArray(),
)
}
}
/**
* Signs the apk file with the given options.
*
* @param signingOptions The options to use for signing.
*/
@Deprecated("Use sign(File, File, SigningOptions) instead.")
fun File.sign(signingOptions: SigningOptions) {
// 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 entries = setOf(ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password))
// Create a new keystore with a new keypair and saves it.
ApkSigner.newKeyStore(entries).apply {
store(
signingOptions.keyStore.outputStream(),
signingOptions.keyStorePassword?.toCharArray(),
)
}
}
val keyStore = readOrNewKeyStore(signingOptions)
@Suppress("DEPRECATION")
ApkSigner.newApkSigner(
keyStore,
signingOptions.alias,
@@ -121,6 +131,24 @@ object ApkUtils {
).signApk(this)
}
/**
* Signs [inputApkFile] with the given options and saves the signed apk to [outputApkFile].
*
* @param inputApkFile The apk file to sign.
* @param outputApkFile The file to save the signed apk to.
* @param signingOptions The options to use for signing.
*/
fun sign(inputApkFile: File, outputApkFile: File, signingOptions: SigningOptions) {
val keyStore = readOrNewKeyStore(signingOptions)
ApkSigner.newApkSigner(
signingOptions.signer,
keyStore,
signingOptions.alias,
signingOptions.password,
).signApk(inputApkFile, outputApkFile)
}
/**
* Options for signing an apk.
*