Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release-bot
fca8a3f4c0 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](51109c4768))
* Sign APKs using `apksig` ([f59ecbc](f59ecbccd1))

### Features

* Increase default expiration date of certificate ([f2bd3f5](f2bd3f5eee))
2024-03-09 03:31:10 +00:00
oSumAtrIX
f59ecbccd1 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.
2024-03-09 04:29:41 +01:00
oSumAtrIX
c92be32607 refactor: Simplify code 2024-03-09 03:25:11 +01:00
oSumAtrIX
51109c4768 fix: Make property private 2024-03-09 03:25:10 +01:00
oSumAtrIX
f2bd3f5eee feat: Increase default expiration date of certificate 2024-03-08 02:30:15 +01:00
oSumAtrIX
cc5ee29d14 build: Set target bytecode level to JVM 11 2024-03-04 19:16:09 +01:00
7 changed files with 178 additions and 46 deletions

View File

@@ -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) # [2.1.0](https://github.com/ReVanced/revanced-library/compare/v2.0.0...v2.1.0) (2024-03-04)

View File

@@ -1,6 +1,8 @@
public final class app/revanced/library/ApkSigner { public final class app/revanced/library/ApkSigner {
public static final field INSTANCE Lapp/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 (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 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/io/OutputStream;Ljava/lang/String;Ljava/util/Set;)V
public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore; public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore;
@@ -25,15 +27,16 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair {
} }
public final class app/revanced/library/ApkSigner$Signer { 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 (Lcom/android/tools/build/apkzlib/zip/ZFile;)V
public final fun signApk (Ljava/io/File;)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 final class app/revanced/library/ApkUtils {
public static final field INSTANCE Lapp/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 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;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 { public final class app/revanced/library/ApkUtils$SigningOptions {

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
alias(libs.plugins.kotlin) alias(libs.plugins.kotlin)
alias(libs.plugins.binary.compatibility.validator) alias(libs.plugins.binary.compatibility.validator)
@@ -27,6 +29,7 @@ dependencies {
implementation(libs.jadb) // Fork with Shell v2 support. implementation(libs.jadb) // Fork with Shell v2 support.
implementation(libs.jackson.module.kotlin) implementation(libs.jackson.module.kotlin)
implementation(libs.apkzlib) implementation(libs.apkzlib)
implementation(libs.apksig)
implementation(libs.bcpkix.jdk15on) implementation(libs.bcpkix.jdk15on)
implementation(libs.guava) implementation(libs.guava)
@@ -43,10 +46,14 @@ tasks {
} }
} }
kotlin { jvmToolchain(11) } kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
java { java {
withSourcesJar() targetCompatibility = JavaVersion.VERSION_11
} }
publishing { publishing {

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 2.1.0 version = 2.2.0-dev.1

View File

@@ -7,6 +7,7 @@ binary-compatibility-validator = "0.14.0"
apkzlib = "8.3.0" apkzlib = "8.3.0"
bcpkix-jdk15on = "1.70" bcpkix-jdk15on = "1.70"
guava = "33.0.0-jre" guava = "33.0.0-jre"
apksig = "8.3.0"
[libraries] [libraries]
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } 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" } apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "apkzlib" }
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" } bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
guava = { module = "com.google.guava:guava", version.ref = "guava" } guava = { module = "com.google.guava:guava", version.ref = "guava" }
apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }
[plugins] [plugins]
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }

View File

@@ -1,5 +1,6 @@
package app.revanced.library 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.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zip.ZFile import com.android.tools.build.apkzlib.zip.ZFile
@@ -43,35 +44,28 @@ object ApkSigner {
*/ */
fun newPrivateKeyCertificatePair( fun newPrivateKeyCertificatePair(
commonName: String = "ReVanced", commonName: String = "ReVanced",
validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24), validUntil: Date = Date(System.currentTimeMillis() + (365.days * 8).inWholeMilliseconds * 24),
): PrivateKeyCertificatePair { ): PrivateKeyCertificatePair {
logger.fine("Creating certificate for $commonName") logger.fine("Creating certificate for $commonName")
// Generate a new key pair. // Generate a new key pair.
val keyPair = val keyPair = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).apply {
KeyPairGenerator.getInstance("RSA").apply { initialize(4096)
initialize(4096) }.generateKeyPair()
}.generateKeyPair()
var serialNumber: BigInteger val contentSigner = JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong())
while (serialNumber < BigInteger.ZERO)
val name = X500Name("CN=$commonName") val name = X500Name("CN=$commonName")
val certificateHolder = X509v3CertificateBuilder(
// Create a new certificate. name,
val certificate = BigInteger.valueOf(SecureRandom().nextLong()),
JcaX509CertificateConverter().getCertificate( Date(System.currentTimeMillis()),
X509v3CertificateBuilder( validUntil,
name, Locale.ENGLISH,
serialNumber, name,
Date(System.currentTimeMillis()), SubjectPublicKeyInfo.getInstance(keyPair.public.encoded),
validUntil, ).build(contentSigner)
Locale.ENGLISH, val certificate = JcaX509CertificateConverter().getCertificate(certificateHolder)
name,
SubjectPublicKeyInfo.getInstance(keyPair.public.encoded),
).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)),
)
return PrivateKeyCertificatePair(keyPair.private, certificate) return PrivateKeyCertificatePair(keyPair.private, certificate)
} }
@@ -189,6 +183,7 @@ object ApkSigner {
/** /**
* Create a new [Signer]. * Create a new [Signer].
* *
* @param signer The name of the signer.
* @param privateKeyCertificatePair The private key and certificate pair to use for signing. * @param privateKeyCertificatePair The private key and certificate pair to use for signing.
* *
* @return The new [Signer]. * @return The new [Signer].
@@ -196,6 +191,38 @@ object ApkSigner {
* @see PrivateKeyCertificatePair * @see PrivateKeyCertificatePair
* @see Signer * @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) = fun newApkSigner(privateKeyCertificatePair: PrivateKeyCertificatePair) =
Signer( Signer(
SigningExtension( SigningExtension(
@@ -212,6 +239,7 @@ object ApkSigner {
/** /**
* Create a new [Signer]. * Create a new [Signer].
* *
* @param signer The name of the signer.
* @param keyStore The keystore to use for signing. * @param keyStore The keystore to use for signing.
* @param keyStoreEntryAlias The alias of the key store entry 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 keyStoreEntryPassword The password for recovering the signing key.
@@ -222,10 +250,36 @@ object ApkSigner {
* @see Signer * @see Signer
*/ */
fun newApkSigner( fun newApkSigner(
signer: String,
keyStore: KeyStore, keyStore: KeyStore,
keyStoreEntryAlias: String, keyStoreEntryAlias: String,
keyStoreEntryPassword: 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. * An entry in a keystore.
@@ -253,23 +307,48 @@ object ApkSigner {
val certificate: X509Certificate, val certificate: X509Certificate,
) )
class Signer internal constructor(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. * Sign an APK file.
* *
* @param apkFile The APK file to sign. * @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. * Sign an APK file.
* *
* @param apkZFile The APK [ZFile] to sign. * @param apkZFile The APK [ZFile] to sign.
*/ */
@Deprecated("This method will be removed in the future.")
fun signApk(apkZFile: ZFile) { fun signApk(apkZFile: ZFile) {
logger.info("Signing ${apkZFile.file.name}") 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() 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. * Signs the apk file with the given options.
* *
* @param signingOptions The options to use for signing. * @param signingOptions The options to use for signing.
*/ */
@Deprecated("Use sign(File, File, SigningOptions) instead.")
fun File.sign(signingOptions: SigningOptions) { fun File.sign(signingOptions: SigningOptions) {
// Get the keystore from the file or create a new one. val keyStore = readOrNewKeyStore(signingOptions)
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(),
)
}
}
@Suppress("DEPRECATION")
ApkSigner.newApkSigner( ApkSigner.newApkSigner(
keyStore, keyStore,
signingOptions.alias, signingOptions.alias,
@@ -121,6 +131,24 @@ object ApkUtils {
).signApk(this) ).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. * Options for signing an apk.
* *