diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api index 9f89984..f76855b 100644 --- a/api/android/revanced-library.api +++ b/api/android/revanced-library.api @@ -29,6 +29,7 @@ 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 fun signApk (Ljava/io/File;Ljava/io/File;Ljava/util/Set;)V } public final class app/revanced/library/ApkUtils { @@ -36,10 +37,12 @@ public final class app/revanced/library/ApkUtils { public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V public final fun newPrivateKeyCertificatePair (Lapp/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; public final fun readPrivateKeyCertificatePairFromKeyStore (Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; + public final fun readSigningLevels (Ljava/io/File;)Ljava/util/Set; 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 fun sign (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V + public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;Ljava/util/Set;)V } public final class app/revanced/library/ApkUtils$KeyStoreDetails { diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api index 5a525d9..22b76ee 100644 --- a/api/jvm/revanced-library.api +++ b/api/jvm/revanced-library.api @@ -29,6 +29,7 @@ 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 fun signApk (Ljava/io/File;Ljava/io/File;Ljava/util/Set;)V } public final class app/revanced/library/ApkUtils { @@ -36,10 +37,12 @@ public final class app/revanced/library/ApkUtils { public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V public final fun newPrivateKeyCertificatePair (Lapp/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; public final fun readPrivateKeyCertificatePairFromKeyStore (Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; + public final fun readSigningLevels (Ljava/io/File;)Ljava/util/Set; 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 fun sign (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V + public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;Ljava/util/Set;)V } public final class app/revanced/library/ApkUtils$KeyStoreDetails { diff --git a/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt b/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt index b653fe4..c7b3fd8 100644 --- a/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt @@ -331,12 +331,24 @@ object ApkSigner { signingExtension = null } - fun signApk(inputApkFile: File, outputApkFile: File) { + fun signApk(inputApkFile: File, outputApkFile: File, signingLevels: Set) { logger.info("Signing APK") + if (signingLevels.isNotEmpty() && signerBuilder != null) { + signerBuilder.setV1SigningEnabled(signingLevels.contains(1)) + signerBuilder.setV2SigningEnabled(signingLevels.contains(2)) + signerBuilder.setV3SigningEnabled(signingLevels.contains(3)) + signerBuilder.setV4SigningEnabled(signingLevels.contains(4)) + } + signerBuilder?.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign() } + @Deprecated("This constructor will be removed in the future.") + fun signApk(inputApkFile: File, outputApkFile: File) { + signApk(inputApkFile, outputApkFile, setOf()) + } + @Deprecated("This constructor will be removed in the future.") internal constructor(signingExtension: SigningExtension) { signerBuilder = null diff --git a/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt b/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt index b56c62c..a21dfa4 100644 --- a/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt +++ b/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt @@ -2,6 +2,7 @@ package app.revanced.library import app.revanced.library.ApkSigner.newPrivateKeyCertificatePair import app.revanced.patcher.PatcherResult +import com.android.apksig.ApkVerifier import com.android.tools.build.apkzlib.zip.AlignmentRules import com.android.tools.build.apkzlib.zip.StoredEntry import com.android.tools.build.apkzlib.zip.ZFile @@ -153,12 +154,14 @@ object ApkUtils { * @param outputApkFile The file to save the signed apk to. * @param signer The name of the signer. * @param keyStoreDetails The details for the keystore. + * @param signingLevels Signature levels, empty to default. */ fun signApk( inputApkFile: File, outputApkFile: File, signer: String, keyStoreDetails: KeyStoreDetails, + signingLevels: Set, ) = ApkSigner.newApkSigner( signer, if (keyStoreDetails.keyStore.exists()) { @@ -166,7 +169,25 @@ object ApkUtils { } else { newPrivateKeyCertificatePair(PrivateKeyCertificatePairDetails(), keyStoreDetails) }, - ).signApk(inputApkFile, outputApkFile) + ).signApk(inputApkFile, outputApkFile, signingLevels) + + /** + * Signs [inputApkFile] with the given options and saves the signed apk to [outputApkFile]. + * If [KeyStoreDetails.keyStore] does not exist, + * a new private key and certificate pair will be created and saved to the keystore. + * + * @param inputApkFile The apk file to sign. + * @param outputApkFile The file to save the signed apk to. + * @param signer The name of the signer. + * @param keyStoreDetails The details for the keystore. + */ + @Deprecated("This method will be removed in the future.") + fun signApk( + inputApkFile: File, + outputApkFile: File, + signer: String, + keyStoreDetails: KeyStoreDetails, + ) = signApk(inputApkFile, outputApkFile, signer, keyStoreDetails, setOf()) @Deprecated("This method will be removed in the future.") private fun readOrNewPrivateKeyCertificatePair( @@ -235,6 +256,27 @@ object ApkUtils { readOrNewPrivateKeyCertificatePair(signingOptions), ) + /** + * Read apk signature levels. + * + * Note: a well-signed apk is required. + */ + fun readSigningLevels(apkFile: File): Set { + val verify = ApkVerifier.Builder(apkFile).build().verify() + if (!verify.isVerified) + return setOf() + val result = mutableSetOf() + if (verify.v1SchemeSigners.isNotEmpty()) + result.add(1) + if (verify.v2SchemeSigners.isNotEmpty()) + result.add(2) + if (verify.v3SchemeSigners.isNotEmpty()) + result.add(3) + if (verify.v4SchemeSigners.isNotEmpty()) + result.add(4) + return result + } + /** * Options for signing an apk. *