本文介绍如何使用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