feat: add option to specify keystore file path

This commit is contained in:
oSumAtrIX 2022-06-22 13:45:52 +02:00
parent 07f6bdf330
commit 9331594706
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
4 changed files with 30 additions and 13 deletions

View File

@ -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
)
) )
} }

View File

@ -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)

View File

@ -0,0 +1,7 @@
package app.revanced.cli.signing
data class SigningOptions(
val cn: String,
val password: String,
val keyStoreFilePath: String
)

View File

@ -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)