mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-06-02 11:26:06 +02:00
90 lines
3.4 KiB
Kotlin
90 lines
3.4 KiB
Kotlin
package app.revanced.lib.signing
|
|
|
|
import com.android.apksig.ApkSigner
|
|
import org.bouncycastle.asn1.x500.X500Name
|
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
|
import org.bouncycastle.cert.X509v3CertificateBuilder
|
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
|
import org.bouncycastle.operator.ContentSigner
|
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
|
import java.io.File
|
|
import java.io.FileInputStream
|
|
import java.io.FileOutputStream
|
|
import java.math.BigInteger
|
|
import java.security.*
|
|
import java.security.cert.X509Certificate
|
|
import java.util.*
|
|
import java.util.logging.Logger
|
|
|
|
class ApkSigner(
|
|
private val signingOptions: SigningOptions
|
|
) {
|
|
private val logger = Logger.getLogger(app.revanced.lib.signing.ApkSigner::class.java.name)
|
|
|
|
private val signer: ApkSigner.Builder
|
|
private val passwordCharArray = signingOptions.password.toCharArray()
|
|
|
|
init {
|
|
Security.addProvider(BouncyCastleProvider())
|
|
|
|
val keyStore = KeyStore.getInstance("BKS", "BC")
|
|
val alias = keyStore.let { store ->
|
|
FileInputStream(signingOptions.keyStoreOutputFilePath.also {
|
|
if (!it.exists()) {
|
|
logger.info("Creating keystore at ${it.absolutePath}")
|
|
newKeystore(it)
|
|
} else {
|
|
logger.info("Using keystore ${it.absolutePath}")
|
|
}
|
|
}).use { fis -> store.load(fis, null) }
|
|
store.aliases().nextElement()
|
|
}
|
|
|
|
with(
|
|
ApkSigner.SignerConfig.Builder(
|
|
signingOptions.commonName,
|
|
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
|
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
|
).build()
|
|
) {
|
|
this@ApkSigner.signer = ApkSigner.Builder(listOf(this))
|
|
signer.setCreatedBy(signingOptions.commonName)
|
|
}
|
|
}
|
|
|
|
private fun newKeystore(out: File) {
|
|
val (publicKey, privateKey) = createKey()
|
|
val privateKS = KeyStore.getInstance("BKS", "BC")
|
|
privateKS.load(null, passwordCharArray)
|
|
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
|
|
privateKS.store(FileOutputStream(out), passwordCharArray)
|
|
}
|
|
|
|
private fun createKey(): Pair<X509Certificate, PrivateKey> {
|
|
val gen = KeyPairGenerator.getInstance("RSA")
|
|
gen.initialize(2048)
|
|
val pair = gen.generateKeyPair()
|
|
var serialNumber: BigInteger
|
|
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
|
val x500Name = X500Name("CN=${signingOptions.commonName}")
|
|
val builder = X509v3CertificateBuilder(
|
|
x500Name,
|
|
serialNumber,
|
|
Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L),
|
|
Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 366L * 30L),
|
|
Locale.ENGLISH,
|
|
x500Name,
|
|
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
|
|
)
|
|
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
|
|
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
|
|
}
|
|
|
|
fun signApk(input: File, output: File) {
|
|
signer.setInputApk(input)
|
|
signer.setOutputApk(output)
|
|
|
|
signer.build().sign()
|
|
}
|
|
} |