Compare commits

..

27 Commits

Author SHA1 Message Date
semantic-release-bot
8d0e4b2470 chore(release): 2.3.0 [skip ci]
# [2.3.0](https://github.com/ReVanced/revanced-library/compare/v2.2.1...v2.3.0) (2024-03-14)

### Bug Fixes

* Support mounting even when Magisk is not installed ([2a30845](2a30845f61))

### Features

* Add utility function around key certificate pairs ([2df3484](2df3484b68))
* Improve exception message ([b15efa4](b15efa41f8))
* Simplify signing utility API ([4c6a636](4c6a6360cf))
2024-03-14 12:16:21 +00:00
oSumAtrIX
4486c59674 chore: Merge branch dev to main (#38) 2024-03-14 13:14:43 +01:00
semantic-release-bot
8e4d5765e1 chore(release): 2.3.0-dev.3 [skip ci]
# [2.3.0-dev.3](https://github.com/ReVanced/revanced-library/compare/v2.3.0-dev.2...v2.3.0-dev.3) (2024-03-14)

### Features

* Improve exception message ([b15efa4](b15efa41f8))
2024-03-14 11:51:48 +00:00
oSumAtrIX
b15efa41f8 feat: Improve exception message 2024-03-14 12:50:20 +01:00
semantic-release-bot
af0aba4a8e chore(release): 2.3.0-dev.2 [skip ci]
# [2.3.0-dev.2](https://github.com/ReVanced/revanced-library/compare/v2.3.0-dev.1...v2.3.0-dev.2) (2024-03-14)

### Features

* Simplify signing utility API ([4c6a636](4c6a6360cf))
2024-03-14 11:40:37 +00:00
oSumAtrIX
4c6a6360cf feat: Simplify signing utility API 2024-03-14 12:39:01 +01:00
oSumAtrIX
80e35270c4 build: Publish sources 2024-03-14 12:21:47 +01:00
semantic-release-bot
13b6a846d7 chore(release): 2.3.0-dev.1 [skip ci]
# [2.3.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.2-dev.1...v2.3.0-dev.1) (2024-03-13)

### Features

* Add utility function around key certificate pairs ([2df3484](2df3484b68))
2024-03-13 22:06:34 +00:00
oSumAtrIX
2df3484b68 feat: Add utility function around key certificate pairs 2024-03-13 23:04:35 +01:00
semantic-release-bot
f2959b610a chore(release): 2.2.2-dev.1 [skip ci]
## [2.2.2-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.1...v2.2.2-dev.1) (2024-03-12)

### Bug Fixes

* Support mounting even when Magisk is not installed ([2a30845](2a30845f61))
2024-03-12 14:46:49 +00:00
oSumAtrIX
2a30845f61 fix: Support mounting even when Magisk is not installed 2024-03-12 15:44:59 +01:00
semantic-release-bot
1b4d87e563 chore(release): 2.2.1 [skip ci]
## [2.2.1](https://github.com/ReVanced/revanced-library/compare/v2.2.0...v2.2.1) (2024-03-09)

### Bug Fixes

* Do not specify a provider to automatically select an available one ([249372c](249372c31f))
2024-03-09 08:49:38 +00:00
oSumAtrIX
7f7dfdd5b5 chore: Merge branch dev to main (#37) 2024-03-09 09:48:22 +01:00
semantic-release-bot
56773fa3d3 chore(release): 2.2.1-dev.1 [skip ci]
## [2.2.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.0...v2.2.1-dev.1) (2024-03-09)

### Bug Fixes

* Do not specify a provider to automatically select an available one ([249372c](249372c31f))
2024-03-09 08:45:38 +00:00
oSumAtrIX
249372c31f fix: Do not specify a provider to automatically select an available one 2024-03-09 09:44:10 +01:00
semantic-release-bot
e7bed8565e chore(release): 2.2.0 [skip ci]
# [2.2.0](https://github.com/ReVanced/revanced-library/compare/v2.1.0...v2.2.0) (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:36:08 +00:00
oSumAtrIX
4a24e2e92d chore: Merge branch dev to main (#35) 2024-03-09 04:34:48 +01:00
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
semantic-release-bot
c4bad9a653 chore(release): 2.1.0 [skip ci]
# [2.1.0](https://github.com/ReVanced/revanced-library/compare/v2.0.0...v2.1.0) (2024-03-04)

### Bug Fixes

* Use `BKS` instead of default signing provider to fix backwards compatibility ([41805fc](41805fcb0b))

### Features

* Mention APK file name when logging aligning ([244ebc2](244ebc2186))
2024-03-04 14:39:47 +00:00
oSumAtrIX
3f713cf76b chore: Merge branch dev to main (#32) 2024-03-04 15:38:10 +01:00
oSumAtrIX
dfe48a24bf ci: Update action 2024-03-04 15:37:06 +01:00
oSumAtrIX
132ad13670 build: Bump dependencies 2024-03-04 15:37:06 +01:00
9 changed files with 503 additions and 136 deletions

View File

@@ -11,7 +11,7 @@ jobs:
name: Dispatch event to documentation repository
if: github.ref == 'refs/heads/main'
steps:
- uses: peter-evans/repository-dispatch@v2
- uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
repository: revanced/revanced-documentation

View File

@@ -1,3 +1,97 @@
# [2.3.0](https://github.com/ReVanced/revanced-library/compare/v2.2.1...v2.3.0) (2024-03-14)
### Bug Fixes
* Support mounting even when Magisk is not installed ([2a30845](https://github.com/ReVanced/revanced-library/commit/2a30845f61d5f77ded7a72ee3d6ab55b4c512d52))
### Features
* Add utility function around key certificate pairs ([2df3484](https://github.com/ReVanced/revanced-library/commit/2df3484b68ed72338a52e76fb4b7ceb9c9c644ed))
* Improve exception message ([b15efa4](https://github.com/ReVanced/revanced-library/commit/b15efa41f8dc7d73865d0eab15be274b9ee3d7a3))
* Simplify signing utility API ([4c6a636](https://github.com/ReVanced/revanced-library/commit/4c6a6360cf83659d1f5c3a7c5710ac54426e9235))
# [2.3.0-dev.3](https://github.com/ReVanced/revanced-library/compare/v2.3.0-dev.2...v2.3.0-dev.3) (2024-03-14)
### Features
* Improve exception message ([b15efa4](https://github.com/ReVanced/revanced-library/commit/b15efa41f8dc7d73865d0eab15be274b9ee3d7a3))
# [2.3.0-dev.2](https://github.com/ReVanced/revanced-library/compare/v2.3.0-dev.1...v2.3.0-dev.2) (2024-03-14)
### Features
* Simplify signing utility API ([4c6a636](https://github.com/ReVanced/revanced-library/commit/4c6a6360cf83659d1f5c3a7c5710ac54426e9235))
# [2.3.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.2-dev.1...v2.3.0-dev.1) (2024-03-13)
### Features
* Add utility function around key certificate pairs ([2df3484](https://github.com/ReVanced/revanced-library/commit/2df3484b68ed72338a52e76fb4b7ceb9c9c644ed))
## [2.2.2-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.1...v2.2.2-dev.1) (2024-03-12)
### Bug Fixes
* Support mounting even when Magisk is not installed ([2a30845](https://github.com/ReVanced/revanced-library/commit/2a30845f61d5f77ded7a72ee3d6ab55b4c512d52))
## [2.2.1](https://github.com/ReVanced/revanced-library/compare/v2.2.0...v2.2.1) (2024-03-09)
### Bug Fixes
* Do not specify a provider to automatically select an available one ([249372c](https://github.com/ReVanced/revanced-library/commit/249372c31f7e7975fc9eacb5361bd07dbc5dfb92))
## [2.2.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.2.0...v2.2.1-dev.1) (2024-03-09)
### Bug Fixes
* Do not specify a provider to automatically select an available one ([249372c](https://github.com/ReVanced/revanced-library/commit/249372c31f7e7975fc9eacb5361bd07dbc5dfb92))
# [2.2.0](https://github.com/ReVanced/revanced-library/compare/v2.1.0...v2.2.0) (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.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)
### Bug Fixes
* Use `BKS` instead of default signing provider to fix backwards compatibility ([41805fc](https://github.com/ReVanced/revanced-library/commit/41805fcb0bdc778fe0870427a0a1caa6d4369cee))
### Features
* Mention APK file name when logging aligning ([244ebc2](https://github.com/ReVanced/revanced-library/commit/244ebc21868c07d1852857f6858c1a53a5561155))
# [2.1.0-dev.2](https://github.com/ReVanced/revanced-library/compare/v2.1.0-dev.1...v2.1.0-dev.2) (2024-03-04)

View File

@@ -1,18 +1,19 @@
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;
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/library/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
public final fun readPrivateKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
}
public final class app/revanced/library/ApkSigner$KeyStoreEntry {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
@@ -25,15 +26,37 @@ 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
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 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 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 class app/revanced/library/ApkUtils$KeyStoreDetails {
public fun <init> (Ljava/io/File;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;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 class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/util/Date;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Date;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCommonName ()Ljava/lang/String;
public final fun getValidUntil ()Ljava/util/Date;
}
public final class app/revanced/library/ApkUtils$SigningOptions {

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlin)
alias(libs.plugins.binary.compatibility.validator)
@@ -27,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)
@@ -43,9 +46,15 @@ tasks {
}
}
kotlin { jvmToolchain(11) }
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
withSourcesJar()
}

View File

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

View File

@@ -4,9 +4,10 @@ jadb = "1.2.1"
kotlin = "1.9.22"
revanced-patcher = "19.3.1"
binary-compatibility-validator = "0.14.0"
apkzlib = "8.2.2"
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" }

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
@@ -18,7 +19,6 @@ import java.security.*
import java.security.cert.X509Certificate
import java.util.*
import java.util.logging.Logger
import kotlin.time.Duration.Companion.days
/**
* Utility class for reading or writing keystore files and entries as well as signing APK files.
@@ -33,84 +33,7 @@ object ApkSigner {
}
}
/**
* 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")
// Generate a new key pair.
val keyPair =
KeyPairGenerator.getInstance("RSA").apply {
initialize(4096)
}.generateKeyPair()
var serialNumber: BigInteger
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()),
validUntil,
Locale.ENGLISH,
name,
SubjectPublicKeyInfo.getInstance(keyPair.public.encoded),
).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)),
)
return PrivateKeyCertificatePair(keyPair.private, certificate)
}
/**
* 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)
}
private fun newKeyStoreInstance() = KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME)
/**
* Create a new keystore with a new keypair.
@@ -120,6 +43,7 @@ object ApkSigner {
* @return The created keystore.
*
* @see KeyStoreEntry
* @see KeyStore
*/
fun newKeyStore(entries: Set<KeyStoreEntry>): KeyStore {
logger.fine("Creating keystore")
@@ -139,24 +63,6 @@ object ApkSigner {
}
}
private fun newKeyStoreInstance() = KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME)
/**
* 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: Set<KeyStoreEntry>,
) = newKeyStore(entries).store(
keyStoreOutputStream,
keyStorePassword.toCharArray(),
)
/**
* Read a keystore from the given [keyStoreInputStream].
*
@@ -166,6 +72,8 @@ object ApkSigner {
* @return The keystore.
*
* @throws IllegalArgumentException If the keystore password is invalid.
*
* @see KeyStore
*/
fun readKeyStore(
keyStoreInputStream: InputStream,
@@ -186,6 +94,144 @@ object ApkSigner {
}
}
/**
* Create a new private key and certificate pair.
*
* @param commonName The common name of the certificate.
* @param validUntil The date until which the certificate is valid.
*
* @return The newly created private key and certificate pair.
*
* @see PrivateKeyCertificatePair
*/
fun newPrivateKeyCertificatePair(
commonName: String,
validUntil: Date,
): PrivateKeyCertificatePair {
logger.fine("Creating certificate for $commonName")
// Generate a new key pair.
val keyPair = KeyPairGenerator.getInstance("RSA").apply {
initialize(4096)
}.generateKeyPair()
val contentSigner = JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)
val name = X500Name("CN=$commonName")
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)
}
/**
* 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.
*
* @see PrivateKeyCertificatePair
* @see KeyStore
*/
fun readPrivateKeyCertificatePair(
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 entry with 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 [Signer].
*
* @param signer The name of the signer.
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*
* @return The new [Signer].
*
* @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(),
),
),
)
/**
* 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.
*/
@Deprecated("This method will be removed in the future.")
fun readKeyCertificatePair(
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = readPrivateKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword)
/**
* 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.
*/
@Deprecated("This method will be removed in the future.")
fun newKeyStore(
keyStoreOutputStream: OutputStream,
keyStorePassword: String?,
entries: Set<KeyStoreEntry>,
) = newKeyStore(entries).store(
keyStoreOutputStream,
keyStorePassword?.toCharArray(),
)
/**
* Create a new [Signer].
*
@@ -196,6 +242,7 @@ object ApkSigner {
* @see PrivateKeyCertificatePair
* @see Signer
*/
@Deprecated("This method will be removed in the future.")
fun newApkSigner(privateKeyCertificatePair: PrivateKeyCertificatePair) =
Signer(
SigningExtension(
@@ -212,6 +259,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.
@@ -221,11 +269,32 @@ object ApkSigner {
* @see KeyStore
* @see Signer
*/
@Deprecated("This method will be removed in the future.")
fun newApkSigner(
signer: String,
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = 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.")
fun newApkSigner(
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = newApkSigner(readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
) = newApkSigner("ReVanced", readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
/**
* An entry in a keystore.
@@ -239,7 +308,7 @@ object ApkSigner {
class KeyStoreEntry(
val alias: String,
val password: String,
val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair(),
val privateKeyCertificatePair: PrivateKeyCertificatePair,
)
/**
@@ -253,23 +322,48 @@ object ApkSigner {
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
}
fun signApk(inputApkFile: File, outputApkFile: File) {
logger.info("Signing APK")
signerBuilder?.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign()
}
@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)
}
}
}

View File

@@ -1,12 +1,15 @@
package app.revanced.library
import app.revanced.library.ApkSigner.newPrivateKeyCertificatePair
import app.revanced.patcher.PatcherResult
import com.android.tools.build.apkzlib.zip.AlignmentRules
import com.android.tools.build.apkzlib.zip.StoredEntry
import com.android.tools.build.apkzlib.zip.ZFile
import com.android.tools.build.apkzlib.zip.ZFileOptions
import java.io.File
import java.util.*
import java.util.logging.Logger
import kotlin.time.Duration.Companion.days
/**
* Utility functions to work with APK files.
@@ -84,7 +87,7 @@ object ApkUtils {
}
}
logger.info("Aligning ${apkFile.name}")
logger.info("Aligning APK")
targetApkZFile.realign()
@@ -92,34 +95,145 @@ object ApkUtils {
}
}
/**
* Creates a new private key and certificate pair and saves it to the keystore in [keyStoreDetails].
*
* @param privateKeyCertificatePairDetails The details for the private key and certificate pair.
* @param keyStoreDetails The details for the keystore.
*
* @return The newly created private key and certificate pair.
*/
@Deprecated("This method will be removed in the future.")
fun newPrivateKeyCertificatePair(
privateKeyCertificatePairDetails: PrivateKeyCertificatePairDetails,
keyStoreDetails: KeyStoreDetails,
) = newPrivateKeyCertificatePair(
privateKeyCertificatePairDetails.commonName,
privateKeyCertificatePairDetails.validUntil,
).also { privateKeyCertificatePair ->
ApkSigner.newKeyStore(
setOf(
ApkSigner.KeyStoreEntry(
keyStoreDetails.alias,
keyStoreDetails.password,
privateKeyCertificatePair,
),
),
).store(
keyStoreDetails.keyStore.outputStream(),
keyStoreDetails.keyStorePassword?.toCharArray(),
)
}
/**
* Reads the private key and certificate pair from an existing keystore.
*
* @param keyStoreDetails The details for the keystore.
*
* @return The private key and certificate pair.
*/
@Deprecated("This method will be removed in the future.")
fun readPrivateKeyCertificatePairFromKeyStore(
keyStoreDetails: KeyStoreDetails,
) = ApkSigner.readPrivateKeyCertificatePair(
ApkSigner.readKeyStore(
keyStoreDetails.keyStore.inputStream(),
keyStoreDetails.keyStorePassword,
),
keyStoreDetails.alias,
keyStoreDetails.password,
)
/**
* 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.
*/
fun signApk(
inputApkFile: File,
outputApkFile: File,
signer: String,
keyStoreDetails: KeyStoreDetails,
) = ApkSigner.newApkSigner(
signer,
if (keyStoreDetails.keyStore.exists()) {
readPrivateKeyCertificatePairFromKeyStore(keyStoreDetails)
} else {
newPrivateKeyCertificatePair(PrivateKeyCertificatePairDetails(), keyStoreDetails)
},
).signApk(inputApkFile, outputApkFile)
@Deprecated("This method will be removed in the future.")
private fun readOrNewPrivateKeyCertificatePair(
signingOptions: SigningOptions,
): ApkSigner.PrivateKeyCertificatePair {
val privateKeyCertificatePairDetails = PrivateKeyCertificatePairDetails(
signingOptions.alias,
PrivateKeyCertificatePairDetails().validUntil,
)
val keyStoreDetails = KeyStoreDetails(
signingOptions.keyStore,
signingOptions.keyStorePassword,
signingOptions.alias,
signingOptions.password,
)
return if (keyStoreDetails.keyStore.exists()) {
readPrivateKeyCertificatePairFromKeyStore(keyStoreDetails)
} else {
newPrivateKeyCertificatePair(privateKeyCertificatePairDetails, keyStoreDetails)
}
}
/**
* 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 signer The name of the signer.
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*/
@Deprecated("This method will be removed in the future.")
fun sign(
inputApkFile: File,
outputApkFile: File,
signer: String,
privateKeyCertificatePair: ApkSigner.PrivateKeyCertificatePair,
) = ApkSigner.newApkSigner(
signer,
privateKeyCertificatePair,
).signApk(inputApkFile, outputApkFile)
/**
* Signs the apk file with the given options.
*
* @param signingOptions The options to use for signing.
*/
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))
@Deprecated("This method will be removed in the future.")
fun File.sign(signingOptions: SigningOptions) = ApkSigner.newApkSigner(
signingOptions.signer,
readOrNewPrivateKeyCertificatePair(signingOptions),
).signApk(this)
// Create a new keystore with a new keypair and saves it.
ApkSigner.newKeyStore(entries).apply {
store(
signingOptions.keyStore.outputStream(),
signingOptions.keyStorePassword?.toCharArray(),
)
}
}
ApkSigner.newApkSigner(
keyStore,
signingOptions.alias,
signingOptions.password,
).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.
*/
@Deprecated("This method will be removed in the future.")
fun sign(inputApkFile: File, outputApkFile: File, signingOptions: SigningOptions) = sign(
inputApkFile,
outputApkFile,
signingOptions.signer,
readOrNewPrivateKeyCertificatePair(signingOptions),
)
/**
* Options for signing an apk.
@@ -130,6 +244,7 @@ object ApkUtils {
* @param password The password for recovering the signing key.
* @param signer The name of the signer.
*/
@Deprecated("This class will be removed in the future.")
class SigningOptions(
val keyStore: File,
val keyStorePassword: String?,
@@ -137,4 +252,30 @@ object ApkUtils {
val password: String = "",
val signer: String = "ReVanced",
)
/**
* Details for a keystore.
*
* @param keyStore The file to save the keystore to.
* @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.
*/
class KeyStoreDetails(
val keyStore: File,
val keyStorePassword: String? = null,
val alias: String = "ReVanced Key",
val password: String = "",
)
/**
* Details for a private key and certificate pair.
*
* @param commonName The common name for the certificate saved in the keystore.
* @param validUntil The date until which the certificate is valid.
*/
class PrivateKeyCertificatePairDetails(
val commonName: String = "ReVanced",
val validUntil: Date = Date(System.currentTimeMillis() + (365.days * 8).inWholeMilliseconds * 24),
)
}

View File

@@ -29,13 +29,17 @@ internal object Constants {
internal val MOUNT_SCRIPT =
"""
#!/system/bin/sh
MAGISKTMP="$( magisk --path )" || MAGISKTMP=/sbin
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
# Use Magisk mirror, if possible.
if command -v magisk &> /dev/null; then
MIRROR="${'$'}(magisk --path)/.magisk/mirror"
fi
# Wait for the system to boot.
until [ "$( getprop sys.boot_completed )" = 1 ]; do sleep 3; done
until [ -d "/sdcard/Android" ]; do sleep 1; done
# Unmount any existing mount as a safety measure
# Unmount any existing mount as a safety measure.
$UMOUNT
base_path="$PATCHED_APK_PATH"
@@ -44,7 +48,7 @@ internal object Constants {
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
# Kill the app to force it to restart the mounted APK in case it's already running
# Kill the app to force it to restart the mounted APK in case it's currently running.
$KILL
""".trimIndent()
}