mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-12-12 21:27:46 +01:00
feat: add option to specify keystore file path
This commit is contained in:
parent
07f6bdf330
commit
9331594706
@ -2,6 +2,7 @@ package app.revanced.cli.command
|
|||||||
|
|
||||||
import app.revanced.cli.patcher.Patcher
|
import app.revanced.cli.patcher.Patcher
|
||||||
import app.revanced.cli.signing.Signing
|
import app.revanced.cli.signing.Signing
|
||||||
|
import app.revanced.cli.signing.SigningOptions
|
||||||
import app.revanced.patcher.PatcherOptions
|
import app.revanced.patcher.PatcherOptions
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.description
|
import app.revanced.patcher.extensions.PatchExtensions.description
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
@ -11,6 +12,8 @@ import picocli.CommandLine.*
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.name
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
|
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
|
||||||
@ -62,6 +65,9 @@ internal object MainCommand : Runnable {
|
|||||||
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
|
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
|
||||||
var cn = "ReVanced"
|
var cn = "ReVanced"
|
||||||
|
|
||||||
|
@Option(names = ["--keystore"], description = ["File path to your keystore"])
|
||||||
|
var keystorePath: String? = null
|
||||||
|
|
||||||
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
|
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
|
||||||
var password = "ReVanced"
|
var password = "ReVanced"
|
||||||
|
|
||||||
@ -111,10 +117,12 @@ internal object MainCommand : Runnable {
|
|||||||
|
|
||||||
if (!args.mount) {
|
if (!args.mount) {
|
||||||
Signing.start(
|
Signing.start(
|
||||||
patchedFile,
|
patchedFile, outputFile, SigningOptions(
|
||||||
outputFile,
|
args.cn,
|
||||||
args.cn,
|
args.password,
|
||||||
args.password,
|
args.keystorePath
|
||||||
|
?: Path(outputFile.parent).resolve("${outputFile.nameWithoutExtension}.keystore").name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import app.revanced.utils.signing.align.ZipAligner
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object Signing {
|
object Signing {
|
||||||
fun start(inputFile: File, outputFile: File, cn: String, password: String) {
|
fun start(inputFile: File, outputFile: File, signingOptions: SigningOptions) {
|
||||||
val cacheDirectory = File(args.pArgs!!.cacheDirectory)
|
val cacheDirectory = File(args.pArgs!!.cacheDirectory)
|
||||||
val alignedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
|
val alignedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
|
||||||
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
|
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
|
||||||
@ -22,7 +22,7 @@ object Signing {
|
|||||||
// sign the alignedOutput and write to signedOutput
|
// sign the alignedOutput and write to signedOutput
|
||||||
// the reason is, in case the signer fails
|
// the reason is, in case the signer fails
|
||||||
// it does not damage the output file
|
// it does not damage the output file
|
||||||
val keyStore = Signer(cn, password).signApk(alignedOutput, signedOutput)
|
val keyStore = Signer(signingOptions).signApk(alignedOutput, signedOutput)
|
||||||
|
|
||||||
// afterwards copy over the file and the keystore to the output
|
// afterwards copy over the file and the keystore to the output
|
||||||
signedOutput.copyTo(outputFile, true)
|
signedOutput.copyTo(outputFile, true)
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.cli.signing
|
||||||
|
|
||||||
|
data class SigningOptions(
|
||||||
|
val cn: String,
|
||||||
|
val password: String,
|
||||||
|
val keyStoreFilePath: String
|
||||||
|
)
|
@ -1,5 +1,7 @@
|
|||||||
package app.revanced.utils.signing
|
package app.revanced.utils.signing
|
||||||
|
|
||||||
|
import app.revanced.cli.command.MainCommand.logger
|
||||||
|
import app.revanced.cli.signing.SigningOptions
|
||||||
import com.android.apksig.ApkSigner
|
import com.android.apksig.ApkSigner
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||||
@ -17,9 +19,9 @@ import java.security.cert.X509Certificate
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal class Signer(
|
internal class Signer(
|
||||||
private val cn: String, password: String
|
private val signingOptions: SigningOptions
|
||||||
) {
|
) {
|
||||||
private val passwordCharArray = password.toCharArray()
|
private val passwordCharArray = signingOptions.password.toCharArray()
|
||||||
private fun newKeystore(out: File) {
|
private fun newKeystore(out: File) {
|
||||||
val (publicKey, privateKey) = createKey()
|
val (publicKey, privateKey) = createKey()
|
||||||
val privateKS = KeyStore.getInstance("BKS", "BC")
|
val privateKS = KeyStore.getInstance("BKS", "BC")
|
||||||
@ -34,7 +36,7 @@ internal class Signer(
|
|||||||
val pair = gen.generateKeyPair()
|
val pair = gen.generateKeyPair()
|
||||||
var serialNumber: BigInteger
|
var serialNumber: BigInteger
|
||||||
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
||||||
val x500Name = X500Name("CN=$cn")
|
val x500Name = X500Name("CN=${signingOptions.cn}")
|
||||||
val builder = X509v3CertificateBuilder(
|
val builder = X509v3CertificateBuilder(
|
||||||
x500Name,
|
x500Name,
|
||||||
serialNumber,
|
serialNumber,
|
||||||
@ -52,21 +54,21 @@ internal class Signer(
|
|||||||
Security.addProvider(BouncyCastleProvider())
|
Security.addProvider(BouncyCastleProvider())
|
||||||
|
|
||||||
// TODO: keystore should be saved securely
|
// TODO: keystore should be saved securely
|
||||||
val ks = File(input.parent, "${output.nameWithoutExtension}.keystore")
|
val ks = File(signingOptions.keyStoreFilePath)
|
||||||
if (!ks.exists()) newKeystore(ks)
|
if (!ks.exists()) newKeystore(ks) else logger.info("Found existing keystore ${ks.nameWithoutExtension}")
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
val keyStore = KeyStore.getInstance("BKS", "BC")
|
||||||
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
|
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
|
||||||
val alias = keyStore.aliases().nextElement()
|
val alias = keyStore.aliases().nextElement()
|
||||||
|
|
||||||
val config = ApkSigner.SignerConfig.Builder(
|
val config = ApkSigner.SignerConfig.Builder(
|
||||||
cn,
|
signingOptions.cn,
|
||||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
||||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
val signer = ApkSigner.Builder(listOf(config))
|
val signer = ApkSigner.Builder(listOf(config))
|
||||||
signer.setCreatedBy(cn)
|
signer.setCreatedBy(signingOptions.cn)
|
||||||
signer.setInputApk(input)
|
signer.setInputApk(input)
|
||||||
signer.setOutputApk(output)
|
signer.setOutputApk(output)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user