ApkVerifier的基础使用

本文介绍如何使用ApkVerifier获取APK的签名信息。

引入

scss 复制代码
implementation("com.android.tools.build:apksig:8.2.2")

使用

ini 复制代码
val verifier: ApkVerifier = ApkVerifier.Builder(inputFile).build()
val result = verifier.verify()
val isSuccess = result.isVerified

inputFile为输入Apk的文件路径,构造一个新的Builder来验证提供的 APK 文件,该验证器旨在密切模仿 Android 平台的行为。这是为了使验证器能够用于检查 APK 的签名是否需要在 Android 上进行验证。

使用verifier.verify()获取结果。如果结果的ApkVerifier.Result.isVerified()返回true ,则可以认为 APK 已验证。验证结果还包括错误、警告以及有关签名者的信息(例如签名证书)

通过ApkVerifier.Result.errors获取错误信息。

vbscript 复制代码
var error = ""
result.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                    .forEach {
                        error += it.toString() + "\n"
                    }

后续使用ApkVerifier.Result.v(1/2/3)SchemeSigners获取签名方案,并获取信息

ini 复制代码
if (result.v1SchemeSigners.isNotEmpty()) {
                    for (signer in result.v1SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(1, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }

V2和V3同理

ini 复制代码
                if (result.v2SchemeSigners.isNotEmpty()) {
                    for (signer in result.v2SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(2, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }
​
                if (result.v3SchemeSigners.isNotEmpty()) {
                    for (signer in result.v3SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(3, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }

这样就可以获取到Apk的签名信息,界面如图

完整代码如下

ini 复制代码
    /**
     * 签名验证
     */
    fun apkVerifier(input: String) {
        launch(Dispatchers.IO) {
            updateApkVerifierState(UIState.Loading)
            val list = ArrayList<model.ApkVerifier>()
            val inputFile = File(input)
            val path = inputFile.path
            val name = inputFile.name
            val verifier: ApkVerifier = ApkVerifier.Builder(inputFile).build()
            try {
                val result = verifier.verify()
                var error = ""
                val isSuccess = result.isVerified
​
                result.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                    .forEach {
                        error += it.toString() + "\n"
                    }
​
                if (result.v1SchemeSigners.isNotEmpty()) {
                    for (signer in result.v1SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(1, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }
​
                if (result.v2SchemeSigners.isNotEmpty()) {
                    for (signer in result.v2SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(2, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }
​
                if (result.v3SchemeSigners.isNotEmpty()) {
                    for (signer in result.v3SchemeSigners) {
                        val cert = signer.certificate ?: continue
                        if (signer.certificate.type == "X.509") {
                            val subject = cert.subjectX500Principal.name
                            val validFrom = cert.notBefore.toString()
                            val validUntil = cert.notAfter.toString()
                            val publicKeyType = (cert.publicKey as? RSAPublicKey)?.algorithm ?: ""
                            val modulus = (cert.publicKey as? RSAPublicKey)?.modulus?.toString(10) ?: ""
                            val signatureType = cert.sigAlgName
                            val md5 = getThumbPrint(cert, "MD5") ?: ""
                            val sha1 = getThumbPrint(cert, "SHA-1") ?: ""
                            val sha256 = getThumbPrint(cert, "SHA-256") ?: ""
                            val apkVerifier = model.ApkVerifier(3, subject, validFrom, validUntil, publicKeyType, modulus, signatureType, md5, sha1, sha256)
                            list.add(apkVerifier)
                        }
                        signer.errors.filter { it.issue == ApkVerifier.Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY }
                            .forEach {
                                error += it.toString() + "\n"
                            }
                    }
                }
​
                if (isSuccess || list.isNotEmpty()) {
                    val apkVerifierResult = ApkVerifierResult(isSuccess, path, name, list)
                    updateApkVerifierState(UIState.Success(apkVerifierResult))
                } else {
                    if (error.isBlank()) {
                        error = "APK签名验证失败"
                    }
                    updateApkVerifierState(UIState.Error(error))
                }
            } catch (e: Exception) {
                updateApkVerifierState(UIState.Error(e.message ?: "APK签名验证失败"))
                e.printStackTrace()
            }
            if (apkVerifierState is UIState.Error) {
                delay(1000)
                updateApkVerifierState(UIState.WAIT)
            }
        }
    }
​
    private fun getThumbPrint(cert: X509Certificate?, type: String?): String? {
        val md = MessageDigest.getInstance(type) // lgtm [java/weak-cryptographic-algorithm]
        val der: ByteArray = cert?.encoded ?: return null
        md.update(der)
        val digest = md.digest()
        return hexify(digest)
    }
​
    private fun hexify(bytes: ByteArray): String {
        val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
        val buf = StringBuilder(bytes.size * 3)
        for (aByte in bytes) {
            buf.append(hexDigits[aByte.toInt() and 0xf0 shr 4])
            buf.append(hexDigits[aByte.toInt() and 0x0f])
            if (bytes.indexOf(aByte) != bytes.size - 1) {
                buf.append(':')
            }
        }
        return buf.toString()
    }

参考:Jadx

相关推荐
明天依旧下着大雨3 天前
PHP8.2.9NTS版本使用composer报错,扩展找不到的问题处理
php·composer
苏琢玉3 天前
顺手写了个地址解析小工具,支持在线用,也能接 PHP 项目里
php·composer
赵大的程序屋17 天前
Jetpack Compose基础组件之 Button
composer
赵大的程序屋17 天前
Jetpack Compose基础组件之 Text
composer
一条九漏鱼1 个月前
数制——FPGA
composer
左小左1 个月前
用Compose撸一个CoordinatorLayout 🔥🔥🔥
android·android jetpack·composer
王大爷~2 个月前
composer 错误汇总
android·php·composer
lihuang3192 个月前
Composer如何通过GitHub Personal Access Token安装私有包:完整教程
github·php·composer
zgscwxd3 个月前
如何安装PHP依赖库 更新2025.2.3
php·composer·phpspreadsheet
zgscwxd3 个月前
如何运行Composer安装PHP包 安装JWT库
composer·jwt库