Skip to content

Commit

Permalink
Use apkSigner instead of zip file contents to extract digest of apk (…
Browse files Browse the repository at this point in the history
…to support all signing schemes)
  • Loading branch information
Ivan Dyatlov committed Jun 24, 2024
1 parent c77f13f commit 714bc6a
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 25 deletions.
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ object Versions {
val slf4jAPI = "1.0.0"
val logbackClassic = "1.2.3"
val axmlParser = "1.0"
val apkSig = "8.5.0"
val bugsnag = "3.6.1"

val androidGradleVersion = "8.0.2"
Expand Down Expand Up @@ -66,6 +67,7 @@ object Libraries {
val ktorAuth = "io.ktor:ktor-client-auth-jvm:${Versions.ktor}"
val ktorApacheClient = "io.ktor:ktor-client-apache:${Versions.ktor}"
val axmlParser = "com.shazam:axmlparser:${Versions.axmlParser}"
val apkSig = "com.android.tools.build:apksig:${Versions.apkSig}"
val gson = "com.google.code.gson:gson:${Versions.gson}"
val apacheCommonsText = "org.apache.commons:commons-text:${Versions.apacheCommonsText}"
val apacheCommonsIO = "commons-io:commons-io:${Versions.apacheCommonsIO}"
Expand Down
1 change: 1 addition & 0 deletions vendor/vendor-android/base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependencies {
implementation(Libraries.kotlinLogging)
implementation(Libraries.dexTestParser)
implementation(Libraries.axmlParser)
implementation(Libraries.apkSig)
implementation(Libraries.jacksonAnnotations)
implementation(Libraries.scalr)
implementation(project(":core"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
package com.malinskiy.marathon.android

import com.android.apksig.ApkVerifier
import com.malinskiy.marathon.io.FileHasher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Paths
import java.security.MessageDigest
import java.util.HexFormat

/**
* Extracts digest of APK contents from signature file
*/
class ApkFileHasher : FileHasher {

override suspend fun getHash(file: File): String = withContext(Dispatchers.IO) {
val zipFile = Paths.get(file.absolutePath)

FileSystems.newFileSystem(zipFile, null as ClassLoader?)
.use { fileSystem ->
val certFile = fileSystem.getPath(SIGNATURE_FILE_PATH)
Files.newInputStream(certFile)
.use {
it
.bufferedReader()
.lineSequence()
.firstOrNull { line -> line.contains(DIGEST_MANIFEST_PROPERTY) }
?.substringAfter(PROPERTY_DELIMITER)
?.trim()
?: throw IllegalArgumentException("Manifest digest not found")
}
}
}
val sha256digest = MessageDigest.getInstance("SHA-256")

private companion object {
private const val SIGNATURE_FILE_PATH = "META-INF/CERT.SF"
private const val DIGEST_MANIFEST_PROPERTY = "-Digest-Manifest: "
private const val PROPERTY_DELIMITER = ": "
override suspend fun getHash(file: File): String = withContext(Dispatchers.IO) {
val result = ApkVerifier.Builder(file).build().verify()
if (result.signerCertificates.isNotEmpty()) {
val certificate = result.signerCertificates.first()
// https://cs.android.com/android/platform/superproject/main/+/main:tools/apksig/src/apksigner/java/com/android/apksigner/ApkSignerTool.java;l=1151;drc=d5137445c0d4067406cb3e38aade5507ff2fcd16
HexFormat.of().formatHex(sha256digest.digest(certificate.encoded))
} else {
throw IllegalArgumentException("Certificate not found")
}
}
}

0 comments on commit 714bc6a

Please sign in to comment.