From 67f7fa061f0b1e6637c521d531a1cc490d50c8f2 Mon Sep 17 00:00:00 2001 From: Dakkshesh Date: Thu, 7 Aug 2025 22:40:49 +0530 Subject: [PATCH] java: CertificiateHacker: Refactor and tidy up a bit - Removed unused boilerplate from initial bring up - Inlined some functions which were fragmented due to some initial plans that were dropped - Moved appropriate functions to CertificateUtils - Tidy up some function and variable names Signed-off-by: Dakkshesh --- .../TrickyStoreOSS/CertificateHacker.kt | 447 ++++++++---------- .../TrickyStoreOSS/CertificateUtils.kt | 56 +++ .../github/beakthoven/TrickyStoreOSS/Main.kt | 2 +- .../interceptors/Keystore2Interceptor.kt | 2 +- .../interceptors/KeystoreInterceptor.kt | 6 +- 5 files changed, 254 insertions(+), 259 deletions(-) diff --git a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateHacker.kt b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateHacker.kt index 47e2846..55a1c57 100644 --- a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateHacker.kt +++ b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateHacker.kt @@ -27,18 +27,13 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.openssl.PEMKeyPair -import org.bouncycastle.openssl.PEMParser -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder -import org.bouncycastle.util.io.pem.PemReader import java.io.ByteArrayInputStream -import java.io.StringReader import java.math.BigInteger import java.nio.charset.StandardCharsets import java.security.* import java.security.cert.Certificate import java.security.cert.CertificateFactory -import java.security.cert.CertificateParsingException import java.security.cert.X509Certificate import java.security.spec.ECGenParameterSpec import java.security.spec.RSAKeyGenParameterSpec @@ -217,14 +212,11 @@ object CertificateHacker { } private val keyboxes = ConcurrentHashMap() - private val leafAlgorithm = ConcurrentHashMap() + private val leafAlgorithms = ConcurrentHashMap() - private const val ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX = 0 - private const val ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX = 1 - private const val ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX = 0 - private const val ATTESTATION_PACKAGE_INFO_VERSION_INDEX = 1 + - fun canHack(): Boolean = keyboxes.isNotEmpty() + fun hasKeyboxes(): Boolean = keyboxes.isNotEmpty() private data class Digest(val digest: ByteArray) { override fun equals(other: Any?): Boolean { @@ -237,46 +229,13 @@ object CertificateHacker { override fun hashCode(): Int = digest.contentHashCode() } - private fun parseKeyPair(keyContent: String): ParseResult { - return try { - PEMParser(StringReader(keyContent.trimLine())).use { parser -> - val pemObject = parser.readObject() - if (pemObject is PEMKeyPair) { - ParseResult.Success(pemObject) - } else { - ParseResult.Error("Invalid PEM key pair format") - } - } - } catch (t: Throwable) { - ParseResult.Error("Failed to parse PEM key pair", t) - } - } + - private fun parseCertificate(certContent: String): ParseResult { - return try { - PemReader(StringReader(certContent.trimLine())).use { reader -> - val pemObject = reader.readPemObject() - val certificate = certificateFactory.generateCertificate( - ByteArrayInputStream(pemObject.content) - ) - ParseResult.Success(certificate) - } - } catch (t: Throwable) { - ParseResult.Error("Failed to parse certificate", t) - } - } - - @Throws(CertificateParsingException::class) - private fun getByteArrayFromAsn1(asn1Encodable: ASN1Encodable): ByteArray { - return when (asn1Encodable) { - is DEROctetString -> asn1Encodable.octets - else -> throw CertificateParsingException("Expected DEROctetString, got ${asn1Encodable::class.simpleName}") - } - } + fun readFromXml(xmlData: String?) { keyboxes.clear() - leafAlgorithm.clear() + leafAlgorithms.clear() if (xmlData == null) { Logger.i("Clearing all keyboxes") @@ -339,18 +298,18 @@ object CertificateHacker { is XmlParser.ParseResult.Error -> throw Exception(certResult.message, certResult.cause) } - when (val certParseResult = parseCertificate(certContent)) { - is ParseResult.Success -> certificateChain.add(certParseResult.data) - is ParseResult.Error -> throw Exception(certParseResult.message, certParseResult.cause) + when (val certParseResult = CertificateUtils.parseCertificate(certContent)) { + is CertificateUtils.ParseResult.Success -> certificateChain.add(certParseResult.data) + is CertificateUtils.ParseResult.Error -> throw Exception(certParseResult.message, certParseResult.cause) } } - val pemKeyPair = when (val keyParseResult = parseKeyPair(privateKeyContent)) { - is ParseResult.Success -> keyParseResult.data - is ParseResult.Error -> throw Exception(keyParseResult.message, keyParseResult.cause) + val pemKeyPair = when (val keyParseResult = CertificateUtils.parseKeyPair(privateKeyContent)) { + is CertificateUtils.ParseResult.Success -> keyParseResult.data + is CertificateUtils.ParseResult.Error -> throw Exception(keyParseResult.message, keyParseResult.cause) } - val keyPair = JcaPEMKeyConverter().getKeyPair(pemKeyPair) + val keyPair = CertificateUtils.convertPemToKeyPair(pemKeyPair) val algorithmName = when (keyboxAlgorithm.lowercase()) { "ecdsa" -> KeyProperties.KEY_ALGORITHM_EC @@ -378,22 +337,65 @@ object CertificateHacker { val extensionBytes = leaf.getExtensionValue(ATTESTATION_OID.id) ?: return certificateChain // No attestation extension, return original + + val leafHolder = X509CertificateHolder(leaf.encoded) + val extension = leafHolder.getExtension(ATTESTATION_OID) + val sequence = ASN1Sequence.getInstance(extension.extnValue.octets) + val encodables = sequence.toArray() + val teeEnforced = encodables[7] as ASN1Sequence - hackCertificateWithAttestation(leaf, certificateChain) + val vector = ASN1EncodableVector() + var rootOfTrust: ASN1Encodable? = null + + teeEnforced.forEach { element -> + val taggedObject = element as ASN1TaggedObject + if (taggedObject.tagNo == 704) { + rootOfTrust = taggedObject.baseObject.toASN1Primitive() + } else { + vector.add(taggedObject) + } + } + + val keybox = keyboxes[leaf.publicKey.algorithm] + ?: throw UnsupportedOperationException("Unsupported algorithm: ${leaf.publicKey.algorithm}") + + val certificates = LinkedList(keybox.certificates) + val builder = X509v3CertificateBuilder( + X509CertificateHolder(certificates[0].encoded).subject, + leafHolder.serialNumber, + leafHolder.notBefore, + leafHolder.notAfter, + leafHolder.subject, + leafHolder.subjectPublicKeyInfo + ) + + val signer = JcaContentSignerBuilder(leaf.sigAlgName).build(keybox.keyPair.private) + + val hackedExtension = createHackedAttestationExtension(rootOfTrust, vector, encodables) + builder.addExtension(hackedExtension) + + leafHolder.extensions.extensionOIDs.forEach { oid -> + if (oid.id != ATTESTATION_OID.id) { + builder.addExtension(leafHolder.getExtension(oid)) + } + } + + certificates.addFirst(JcaX509CertificateConverter().getCertificate(builder.build(signer))) + certificates.toTypedArray() } catch (t: Throwable) { Logger.e("Failed to hack certificate chain", t) certificateChain } } - fun hackCertificateChainCA(caList: ByteArray?, alias: String, uid: Int): ByteArray { + fun hackCACertificateChain(caList: ByteArray?, alias: String, uid: Int): ByteArray { if (caList == null) { throw UnsupportedOperationException("CA list is null!") } return try { val key = KeyIdentifier(alias, uid) - val algorithm = leafAlgorithm.remove(key) + val algorithm = leafAlgorithms.remove(key) ?: throw UnsupportedOperationException("No algorithm found for key $key") val keybox = keyboxes[algorithm] @@ -406,7 +408,7 @@ object CertificateHacker { } } - fun hackCertificateChainUSR(certificate: ByteArray?, alias: String, uid: Int): ByteArray { + fun hackUserCertificate(certificate: ByteArray?, alias: String, uid: Int): ByteArray { if (certificate == null) { throw UnsupportedOperationException("Leaf certificate is null!") } @@ -420,191 +422,8 @@ object CertificateHacker { ?: return certificate // No attestation extension, return original val keyIdentifier = KeyIdentifier(alias, uid) - leafAlgorithm[keyIdentifier] = leaf.publicKey.algorithm + leafAlgorithms[keyIdentifier] = leaf.publicKey.algorithm - hackSingleCertificate(leaf)?.encoded ?: certificate - } catch (t: Throwable) { - Logger.e("Failed to hack user certificate", t) - certificate - } - } - - fun generateKeyPair(params: KeyGenParameters): KeyPair? { - return try { - when (params.algorithm) { - Algorithm.EC -> { - Logger.d("Generating EC keypair of size ${params.keySize}") - buildECKeyPair(params) - } - Algorithm.RSA -> { - Logger.d("Generating RSA keypair of size ${params.keySize}") - buildRSAKeyPair(params) - } - else -> { - Logger.e("Unsupported algorithm: ${params.algorithm}") - null - } - } - } catch (t: Throwable) { - Logger.e("Failed to generate key pair", t) - null - } - } - - fun generateChain(uid: Int, params: KeyGenParameters, keyPair: KeyPair): List? { - return try { - val keybox = getKeyboxForAlgorithm(params.algorithm) - ?: return null - - val issuer = X509CertificateHolder(keybox.certificates[0].encoded).subject - val leaf = buildCertificate(keyPair, keybox, params, issuer, uid) - - val chain = mutableListOf().apply { - add(leaf) - addAll(keybox.certificates) - } - - CertificateUtils.run { chain.toByteArrayList() } - } catch (t: Throwable) { - Logger.e("Failed to generate certificate chain", t) - null - } - } - - fun generateKeyPair( - uid: Int, - descriptor: KeyDescriptor, - attestKeyDescriptor: KeyDescriptor?, - params: KeyGenParameters - ): Pair>? { - Logger.i("Requested KeyPair with alias: ${descriptor.alias}") - - val isAttestPurpose = attestKeyDescriptor != null - if (isAttestPurpose) { - Logger.i("Requested KeyPair with attestKey: ${attestKeyDescriptor?.alias}") - } - - return try { - val keyPair = generateKeyPair(params) ?: return null - val keybox = getKeyboxForAlgorithm(params.algorithm) ?: return null - - val (rootKeyPair, issuer) = if (isAttestPurpose) { - val attestInfo = getAttestationKeyInfo(uid, attestKeyDescriptor!!) - if (attestInfo != null) { - attestInfo.first to attestInfo.second - } else { - keybox.keyPair to X509CertificateHolder(keybox.certificates[0].encoded).subject - } - } else { - keybox.keyPair to X509CertificateHolder(keybox.certificates[0].encoded).subject - } - - val leaf = buildCertificate(keyPair, keybox, params, issuer, uid, rootKeyPair) - val chain = if (isAttestPurpose) mutableListOf() else mutableListOf().apply { addAll(keybox.certificates) } - chain.add(0, leaf) - - Logger.d("Successfully generated certificate for alias: ${descriptor.alias}") - Pair(keyPair, chain) - } catch (t: Throwable) { - Logger.e("Failed to generate key pair with certificates", t) - null - } - } - - private fun buildECKeyPair(params: KeyGenParameters): KeyPair { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) - Security.addProvider(BouncyCastleProvider()) - - val spec = ECGenParameterSpec(params.ecCurveName) - val keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME) - keyPairGenerator.initialize(spec) - return keyPairGenerator.generateKeyPair() - } - - private fun buildRSAKeyPair(params: KeyGenParameters): KeyPair { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) - Security.addProvider(BouncyCastleProvider()) - - val spec = RSAKeyGenParameterSpec(params.keySize, params.rsaPublicExponent) - val keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME) - keyPairGenerator.initialize(spec) - return keyPairGenerator.generateKeyPair() - } - - private fun getKeyboxForAlgorithm(algorithm: Int): KeyBox? { - val algorithmName = when (algorithm) { - Algorithm.EC -> KeyProperties.KEY_ALGORITHM_EC - Algorithm.RSA -> KeyProperties.KEY_ALGORITHM_RSA - else -> { - Logger.e("Unsupported algorithm: $algorithm") - return null - } - } - return keyboxes[algorithmName] - } - - private fun getAttestationKeyInfo(uid: Int, attestKeyDescriptor: KeyDescriptor): Pair? { - Logger.d("Looking for attestation key: uid=$uid alias=${attestKeyDescriptor.alias}") - - val keyInfo = SecurityLevelInterceptor.getKeyPairs(uid, attestKeyDescriptor.alias) - return if (keyInfo != null) { - val issuer = X509CertificateHolder(keyInfo.second[0].encoded).subject - Pair(keyInfo.first, issuer) - } else { - Logger.e("Attestation key info not found, falling back to default keybox") - null - } - } - - private fun hackCertificateWithAttestation(leaf: X509Certificate, originalChain: Array): Array { - val leafHolder = X509CertificateHolder(leaf.encoded) - val extension = leafHolder.getExtension(ATTESTATION_OID) - val sequence = ASN1Sequence.getInstance(extension.extnValue.octets) - val encodables = sequence.toArray() - val teeEnforced = encodables[7] as ASN1Sequence - - val vector = ASN1EncodableVector() - var rootOfTrust: ASN1Encodable? = null - - teeEnforced.forEach { element -> - val taggedObject = element as ASN1TaggedObject - if (taggedObject.tagNo == 704) { - rootOfTrust = taggedObject.baseObject.toASN1Primitive() - } else { - vector.add(taggedObject) - } - } - - val keybox = keyboxes[leaf.publicKey.algorithm] - ?: throw UnsupportedOperationException("Unsupported algorithm: ${leaf.publicKey.algorithm}") - - val certificates = LinkedList(keybox.certificates) - val builder = X509v3CertificateBuilder( - X509CertificateHolder(certificates[0].encoded).subject, - leafHolder.serialNumber, - leafHolder.notBefore, - leafHolder.notAfter, - leafHolder.subject, - leafHolder.subjectPublicKeyInfo - ) - - val signer = JcaContentSignerBuilder(leaf.sigAlgName).build(keybox.keyPair.private) - - val hackedExtension = createHackedAttestationExtension(rootOfTrust, vector, encodables) - builder.addExtension(hackedExtension) - - leafHolder.extensions.extensionOIDs.forEach { oid -> - if (oid.id != ATTESTATION_OID.id) { - builder.addExtension(leafHolder.getExtension(oid)) - } - } - - certificates.addFirst(JcaX509CertificateConverter().getCertificate(builder.build(signer))) - return certificates.toTypedArray() - } - - private fun hackSingleCertificate(leaf: X509Certificate): Certificate? { - return try { val leafHolder = X509CertificateHolder(leaf.encoded) val extension = leafHolder.getExtension(ATTESTATION_OID) val sequence = ASN1Sequence.getInstance(extension.extnValue.octets) @@ -645,10 +464,131 @@ object CertificateHacker { builder.addExtension(leafHolder.getExtension(oid)) } } - - JcaX509CertificateConverter().getCertificate(builder.build(signer)) + + JcaX509CertificateConverter().getCertificate(builder.build(signer)).encoded } catch (t: Throwable) { - Logger.e("Failed to hack single certificate", t) + Logger.e("Failed to hack user certificate", t) + certificate + } + } + + fun generateKeyPair(params: KeyGenParameters): KeyPair? = runCatching { + when (params.algorithm) { + Algorithm.EC -> { + Logger.d("Generating EC keypair of size ${params.keySize}") + buildECKeyPair(params) + } + Algorithm.RSA -> { + Logger.d("Generating RSA keypair of size ${params.keySize}") + buildRSAKeyPair(params) + } + else -> { + Logger.e("Unsupported algorithm: ${params.algorithm}") + null + } + } + }.onFailure { + Logger.e("Failed to generate key pair", it) + }.getOrNull() + + fun generateChain(uid: Int, params: KeyGenParameters, keyPair: KeyPair): List? = runCatching { + val keybox = getKeyboxForAlgorithm(params.algorithm) ?: return null + + val issuer = X509CertificateHolder(keybox.certificates[0].encoded).subject + val leaf = buildCertificate(keyPair, keybox, params, issuer, uid) + + val chain = buildList { + add(leaf) + addAll(keybox.certificates) + } + + CertificateUtils.run { chain.toByteArrayList() } + }.onFailure { + Logger.e("Failed to generate certificate chain", it) + }.getOrNull() + + fun generateKeyPair( + uid: Int, + descriptor: KeyDescriptor, + attestKeyDescriptor: KeyDescriptor?, + params: KeyGenParameters + ): Pair>? = runCatching { + Logger.i("Requested KeyPair with alias: ${descriptor.alias}") + + val hasAttestKey = attestKeyDescriptor != null + if (hasAttestKey) { + Logger.i("Requested KeyPair with attestKey: ${attestKeyDescriptor?.alias}") + } + + val keyPair = generateKeyPair(params) ?: return null + val keybox = getKeyboxForAlgorithm(params.algorithm) ?: return null + + val (signingKeyPair, issuer) = if (hasAttestKey) { + getAttestationKeyInfo(uid, attestKeyDescriptor!!)?.let { + it.first to it.second + } ?: (keybox.keyPair to X509CertificateHolder(keybox.certificates[0].encoded).subject) + } else { + keybox.keyPair to X509CertificateHolder(keybox.certificates[0].encoded).subject + } + + val leaf = buildCertificate(keyPair, keybox, params, issuer, uid, signingKeyPair) + val chain = buildList { + add(leaf) + if (!hasAttestKey) { + addAll(keybox.certificates) + } + } + + Logger.d("Successfully generated certificate for alias: ${descriptor.alias}") + Pair(keyPair, chain) + }.onFailure { + Logger.e("Failed to generate key pair with certificates", it) + }.getOrNull() + + private fun buildECKeyPair(params: KeyGenParameters): KeyPair { + setupBouncyCastle() + val spec = ECGenParameterSpec(params.ecCurveName) + val keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME) + keyPairGenerator.initialize(spec) + return keyPairGenerator.generateKeyPair() + } + + private fun buildRSAKeyPair(params: KeyGenParameters): KeyPair { + setupBouncyCastle() + val spec = RSAKeyGenParameterSpec(params.keySize, params.rsaPublicExponent) + val keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME) + keyPairGenerator.initialize(spec) + return keyPairGenerator.generateKeyPair() + } + + private fun setupBouncyCastle() { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) + Security.addProvider(BouncyCastleProvider()) + } + + private fun mapAlgorithmToName(algorithm: Int): String? = when (algorithm) { + Algorithm.EC -> KeyProperties.KEY_ALGORITHM_EC + Algorithm.RSA -> KeyProperties.KEY_ALGORITHM_RSA + else -> { + Logger.e("Unsupported algorithm: $algorithm") + null + } + } + + private fun getKeyboxForAlgorithm(algorithm: Int): KeyBox? { + val algorithmName = mapAlgorithmToName(algorithm) ?: return null + return keyboxes[algorithmName] + } + + private fun getAttestationKeyInfo(uid: Int, attestKeyDescriptor: KeyDescriptor): Pair? { + Logger.d("Looking for attestation key: uid=$uid alias=${attestKeyDescriptor.alias}") + + val keyInfo = SecurityLevelInterceptor.getKeyPairs(uid, attestKeyDescriptor.alias) + return if (keyInfo != null) { + val issuer = X509CertificateHolder(keyInfo.second[0].encoded).subject + Pair(keyInfo.first, issuer) + } else { + Logger.e("Attestation key info not found, falling back to default keybox") null } } @@ -663,7 +603,7 @@ object CertificateHacker { try { if (originalRootOfTrust is ASN1Sequence) { - verifiedBootHash = getByteArrayFromAsn1(originalRootOfTrust.getObjectAt(3)) + verifiedBootHash = CertificateUtils.getByteArrayFromAsn1(originalRootOfTrust.getObjectAt(3)) } } catch (t: Throwable) { Logger.e("Failed to get verified boot hash from original, using generated", t) @@ -715,11 +655,12 @@ object CertificateHacker { builder.addExtension(Extension.keyUsage, true, KeyUsage(KeyUsage.keyCertSign)) builder.addExtension(createAttestationExtension(params, uid)) - val contentSigner = when (params.algorithm) { - Algorithm.EC -> JcaContentSignerBuilder("SHA256withECDSA").build(signingKeyPair.private) - Algorithm.RSA -> JcaContentSignerBuilder("SHA256withRSA").build(signingKeyPair.private) + val signerAlgorithm = when (params.algorithm) { + Algorithm.EC -> "SHA256withECDSA" + Algorithm.RSA -> "SHA256withRSA" else -> throw IllegalArgumentException("Unsupported algorithm: ${params.algorithm}") } + val contentSigner = JcaContentSignerBuilder(signerAlgorithm).build(signingKeyPair.private) return JcaX509CertificateConverter().getCertificate(builder.build(contentSigner)) } @@ -737,10 +678,10 @@ object CertificateHacker { ) val rootOfTrustSeq = DERSequence(rootOfTrustEncodables) - val purpose = DERSet(fromIntList(params.purpose)) + val purpose = DERSet(params.purpose.map { ASN1Integer(it.toLong()) }.toTypedArray()) val algorithm = ASN1Integer(params.algorithm.toLong()) val keySize = ASN1Integer(params.keySize.toLong()) - val digest = DERSet(fromIntList(params.digest)) + val digest = DERSet(params.digest.map { ASN1Integer(it.toLong()) }.toTypedArray()) val ecCurve = ASN1Integer(params.ecCurve.toLong()) val noAuthRequired = DERNull.INSTANCE @@ -798,9 +739,7 @@ object CertificateHacker { } } - private fun fromIntList(list: List): Array { - return list.map { ASN1Integer(it.toLong()) }.toTypedArray() - } + private fun getAsn1OctetString( teeEnforcedEncodables: Array, diff --git a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateUtils.kt b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateUtils.kt index de932fc..dc8dfca 100644 --- a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateUtils.kt +++ b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/CertificateUtils.kt @@ -9,11 +9,20 @@ import android.system.keystore2.KeyEntryResponse import android.system.keystore2.KeyMetadata import android.util.Log import io.github.beakthoven.TrickyStoreOSS.CertificateUtils.putCertificateChain +import org.bouncycastle.asn1.ASN1Encodable +import org.bouncycastle.asn1.DEROctetString +import org.bouncycastle.openssl.PEMKeyPair +import org.bouncycastle.openssl.PEMParser +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter +import org.bouncycastle.util.io.pem.PemReader import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream +import java.io.StringReader +import java.security.KeyPair import java.security.cert.Certificate import java.security.cert.CertificateException import java.security.cert.CertificateFactory +import java.security.cert.CertificateParsingException import java.security.cert.X509Certificate object CertificateUtils { @@ -34,6 +43,11 @@ object CertificateUtils { } } + sealed class ParseResult { + data class Success(val data: T) : ParseResult() + data class Error(val message: String, val cause: Throwable? = null) : ParseResult() + } + fun ByteArray?.toCertificate(): X509Certificate? { return this?.let { bytes -> try { @@ -124,6 +138,48 @@ object CertificateUtils { } } } + + // Certificate parsing utilities + fun parseKeyPair(keyContent: String): ParseResult { + return try { + PEMParser(StringReader(keyContent.trimLine())).use { parser -> + val pemObject = parser.readObject() + if (pemObject is PEMKeyPair) { + ParseResult.Success(pemObject) + } else { + ParseResult.Error("Invalid PEM key pair format") + } + } + } catch (t: Throwable) { + ParseResult.Error("Failed to parse PEM key pair", t) + } + } + + fun parseCertificate(certContent: String): ParseResult { + return try { + PemReader(StringReader(certContent.trimLine())).use { reader -> + val pemObject = reader.readPemObject() + val certificate = CertificateFactory.getInstance("X.509").generateCertificate( + ByteArrayInputStream(pemObject.content) + ) + ParseResult.Success(certificate) + } + } catch (t: Throwable) { + ParseResult.Error("Failed to parse certificate", t) + } + } + + fun convertPemToKeyPair(pemKeyPair: PEMKeyPair): KeyPair { + return JcaPEMKeyConverter().getKeyPair(pemKeyPair) + } + + @Throws(CertificateParsingException::class) + fun getByteArrayFromAsn1(asn1Encodable: ASN1Encodable): ByteArray { + return when (asn1Encodable) { + is DEROctetString -> asn1Encodable.octets + else -> throw CertificateParsingException("Expected DEROctetString, got ${asn1Encodable::class.simpleName}") + } + } } fun ByteArray?.toX509Certificate(): X509Certificate? = CertificateUtils.run { this@toX509Certificate.toCertificate() } diff --git a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/Main.kt b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/Main.kt index eab636a..d0a110f 100644 --- a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/Main.kt +++ b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/Main.kt @@ -8,8 +8,8 @@ package io.github.beakthoven.TrickyStoreOSS import android.os.Build import io.github.beakthoven.TrickyStoreOSS.core.config.Config import io.github.beakthoven.TrickyStoreOSS.core.logging.Logger -import io.github.beakthoven.TrickyStoreOSS.interceptors.KeystoreInterceptor import io.github.beakthoven.TrickyStoreOSS.interceptors.Keystore2Interceptor +import io.github.beakthoven.TrickyStoreOSS.interceptors.KeystoreInterceptor private const val RETRY_DELAY_MS = 1000L private const val SERVICE_SLEEP_MS = 1000000L diff --git a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/Keystore2Interceptor.kt b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/Keystore2Interceptor.kt index a21016f..369f1cf 100644 --- a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/Keystore2Interceptor.kt +++ b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/Keystore2Interceptor.kt @@ -74,7 +74,7 @@ object Keystore2Interceptor : BaseKeystoreInterceptor() { data: Parcel ): Result { if (code == getKeyEntryTransaction) { - if (CertificateHacker.canHack()) { + if (CertificateHacker.hasKeyboxes()) { Logger.d("intercept pre $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()}") kotlin.runCatching { data.enforceInterface(IKeystoreService.DESCRIPTOR) diff --git a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/KeystoreInterceptor.kt b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/KeystoreInterceptor.kt index 0aa15c7..8c9b697 100644 --- a/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/KeystoreInterceptor.kt +++ b/app/src/main/java/io/github/beakthoven/TrickyStoreOSS/interceptors/KeystoreInterceptor.kt @@ -64,7 +64,7 @@ object KeystoreInterceptor : BaseKeystoreInterceptor() { callingPid: Int, data: Parcel ): Result { - if (CertificateHacker.canHack()) { + if (CertificateHacker.hasKeyboxes()) { if (code == getTransaction) { if (Config.needHack(callingUid)) { return Continue @@ -218,12 +218,12 @@ object KeystoreInterceptor : BaseKeystoreInterceptor() { var response = reply.createByteArray() when { alias.startsWith(Credentials.USER_CERTIFICATE) -> { - response = CertificateHacker.hackCertificateChainUSR(response!!, alias.extractAlias(), callingUid) + response = CertificateHacker.hackUserCertificate(response!!, alias.extractAlias(), callingUid) Logger.i("Hacked leaf certificate for uid=$callingUid") return createByteArrayReply(response) } alias.startsWith(Credentials.CA_CERTIFICATE) -> { - response = CertificateHacker.hackCertificateChainCA(response!!, alias.extractAlias(), callingUid) + response = CertificateHacker.hackCACertificateChain(response!!, alias.extractAlias(), callingUid) Logger.i("Hacked CA certificate chain for uid=$callingUid") return createByteArrayReply(response) }