From cc5ee29d1427a984f008873916abde71b9850f20 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 4 Mar 2024 19:15:07 +0100 Subject: [PATCH 1/6] build: Set target bytecode level to JVM 11 --- build.gradle.kts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4ea1f71..ffcec6d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.kotlin) alias(libs.plugins.binary.compatibility.validator) @@ -43,10 +45,14 @@ tasks { } } -kotlin { jvmToolchain(11) } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } +} java { - withSourcesJar() + targetCompatibility = JavaVersion.VERSION_11 } publishing { From f2bd3f5eeee14ca32094be0d41c32b231a16bcc3 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 8 Mar 2024 02:30:15 +0100 Subject: [PATCH 2/6] feat: Increase default expiration date of certificate --- src/main/kotlin/app/revanced/library/ApkSigner.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/main/kotlin/app/revanced/library/ApkSigner.kt index e7c71d9..6e14bbb 100644 --- a/src/main/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/library/ApkSigner.kt @@ -43,7 +43,7 @@ object ApkSigner { */ fun newPrivateKeyCertificatePair( commonName: String = "ReVanced", - validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24), + validUntil: Date = Date(System.currentTimeMillis() + (365.days * 8).inWholeMilliseconds * 24), ): PrivateKeyCertificatePair { logger.fine("Creating certificate for $commonName") From 51109c476837828535dcd395a5222d2fcf7fc22c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 8 Mar 2024 21:52:37 +0100 Subject: [PATCH 3/6] fix: Make property private --- api/revanced-library.api | 1 - src/main/kotlin/app/revanced/library/ApkSigner.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/revanced-library.api b/api/revanced-library.api index 5164c17..c6ff752 100644 --- a/api/revanced-library.api +++ b/api/revanced-library.api @@ -25,7 +25,6 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair { } public final class app/revanced/library/ApkSigner$Signer { - public final fun getSigningExtension ()Lcom/android/tools/build/apkzlib/sign/SigningExtension; public final fun signApk (Lcom/android/tools/build/apkzlib/zip/ZFile;)V public final fun signApk (Ljava/io/File;)V } diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/main/kotlin/app/revanced/library/ApkSigner.kt index 6e14bbb..b0ad9ad 100644 --- a/src/main/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/library/ApkSigner.kt @@ -253,7 +253,7 @@ object ApkSigner { val certificate: X509Certificate, ) - class Signer internal constructor(val signingExtension: SigningExtension) { + class Signer internal constructor(private val signingExtension: SigningExtension) { /** * Sign an APK file. * From c92be32607087b7e0a61e8db1fa49ae136f7bb5d Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 8 Mar 2024 23:18:52 +0100 Subject: [PATCH 4/6] refactor: Simplify code --- .../kotlin/app/revanced/library/ApkSigner.kt | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/main/kotlin/app/revanced/library/ApkSigner.kt index b0ad9ad..9fe0aea 100644 --- a/src/main/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/library/ApkSigner.kt @@ -48,30 +48,23 @@ object ApkSigner { logger.fine("Creating certificate for $commonName") // Generate a new key pair. - val keyPair = - KeyPairGenerator.getInstance("RSA").apply { - initialize(4096) - }.generateKeyPair() + val keyPair = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).apply { + initialize(4096) + }.generateKeyPair() - var serialNumber: BigInteger - do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) - while (serialNumber < BigInteger.ZERO) + val contentSigner = JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private) val name = X500Name("CN=$commonName") - - // Create a new certificate. - val certificate = - JcaX509CertificateConverter().getCertificate( - X509v3CertificateBuilder( - name, - serialNumber, - Date(System.currentTimeMillis()), - validUntil, - Locale.ENGLISH, - name, - SubjectPublicKeyInfo.getInstance(keyPair.public.encoded), - ).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)), - ) + val certificateHolder = X509v3CertificateBuilder( + name, + BigInteger.valueOf(SecureRandom().nextLong()), + Date(System.currentTimeMillis()), + validUntil, + Locale.ENGLISH, + name, + SubjectPublicKeyInfo.getInstance(keyPair.public.encoded), + ).build(contentSigner) + val certificate = JcaX509CertificateConverter().getCertificate(certificateHolder) return PrivateKeyCertificatePair(keyPair.private, certificate) } From f59ecbccd14a08d87d4f18c3c0cc47a884088b99 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 9 Mar 2024 00:56:15 +0100 Subject: [PATCH 5/6] 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. --- api/revanced-library.api | 4 + build.gradle.kts | 1 + gradle/libs.versions.toml | 2 + .../kotlin/app/revanced/library/ApkSigner.kt | 94 ++++++++++++++++++- .../kotlin/app/revanced/library/ApkUtils.kt | 60 ++++++++---- 5 files changed, 141 insertions(+), 20 deletions(-) diff --git a/api/revanced-library.api b/api/revanced-library.api index c6ff752..91e2553 100644 --- a/api/revanced-library.api +++ b/api/revanced-library.api @@ -1,6 +1,8 @@ public final class app/revanced/library/ApkSigner { public static final field INSTANCE Lapp/revanced/library/ApkSigner; public final fun newApkSigner (Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer; + public final fun newApkSigner (Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer; + public final fun newApkSigner (Ljava/lang/String;Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer; public final fun newApkSigner (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer; public final fun newKeyStore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/Set;)V public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore; @@ -27,12 +29,14 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair { public final class app/revanced/library/ApkSigner$Signer { public final fun signApk (Lcom/android/tools/build/apkzlib/zip/ZFile;)V public final fun signApk (Ljava/io/File;)V + public final fun signApk (Ljava/io/File;Ljava/io/File;)V } public final class app/revanced/library/ApkUtils { public static final field INSTANCE Lapp/revanced/library/ApkUtils; public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V public final fun sign (Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V + public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V } public final class app/revanced/library/ApkUtils$SigningOptions { diff --git a/build.gradle.kts b/build.gradle.kts index ffcec6d..25048b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(libs.jadb) // Fork with Shell v2 support. implementation(libs.jackson.module.kotlin) implementation(libs.apkzlib) + implementation(libs.apksig) implementation(libs.bcpkix.jdk15on) implementation(libs.guava) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e0fc88e..04a06fc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ binary-compatibility-validator = "0.14.0" apkzlib = "8.3.0" bcpkix-jdk15on = "1.70" guava = "33.0.0-jre" +apksig = "8.3.0" [libraries] jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } @@ -17,6 +18,7 @@ revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "re apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "apkzlib" } bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" } guava = { module = "com.google.guava:guava", version.ref = "guava" } +apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" } [plugins] binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/main/kotlin/app/revanced/library/ApkSigner.kt index 9fe0aea..dce445e 100644 --- a/src/main/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/library/ApkSigner.kt @@ -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() } } } diff --git a/src/main/kotlin/app/revanced/library/ApkUtils.kt b/src/main/kotlin/app/revanced/library/ApkUtils.kt index 34bc8f9..ab73bc3 100644 --- a/src/main/kotlin/app/revanced/library/ApkUtils.kt +++ b/src/main/kotlin/app/revanced/library/ApkUtils.kt @@ -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. * From fca8a3f4c09043bde15631e0bcf5eaf843ae53be Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 9 Mar 2024 03:31:10 +0000 Subject: [PATCH 6/6] chore(release): 2.2.0-dev.1 [skip ci] # [2.2.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.1.0...v2.2.0-dev.1) (2024-03-09) ### Bug Fixes * Make property private ([51109c4](https://github.com/ReVanced/revanced-library/commit/51109c476837828535dcd395a5222d2fcf7fc22c)) * Sign APKs using `apksig` ([f59ecbc](https://github.com/ReVanced/revanced-library/commit/f59ecbccd14a08d87d4f18c3c0cc47a884088b99)) ### Features * Increase default expiration date of certificate ([f2bd3f5](https://github.com/ReVanced/revanced-library/commit/f2bd3f5eeee14ca32094be0d41c32b231a16bcc3)) --- CHANGELOG.md | 13 +++++++++++++ gradle.properties | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf5cf5..b1142ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# [2.2.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.1.0...v2.2.0-dev.1) (2024-03-09) + + +### Bug Fixes + +* Make property private ([51109c4](https://github.com/ReVanced/revanced-library/commit/51109c476837828535dcd395a5222d2fcf7fc22c)) +* Sign APKs using `apksig` ([f59ecbc](https://github.com/ReVanced/revanced-library/commit/f59ecbccd14a08d87d4f18c3c0cc47a884088b99)) + + +### Features + +* Increase default expiration date of certificate ([f2bd3f5](https://github.com/ReVanced/revanced-library/commit/f2bd3f5eeee14ca32094be0d41c32b231a16bcc3)) + # [2.1.0](https://github.com/ReVanced/revanced-library/compare/v2.0.0...v2.1.0) (2024-03-04) diff --git a/gradle.properties b/gradle.properties index cabe972..995c9cc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.1.0 +version = 2.2.0-dev.1