Android设备所有者应用获取下载APK签名证书遇null问题求助
问题分析与解决方案
原因分析
1. keytool报错的原因
APK存在两种主流签名方案:
- v1(JAR签名):传统基于JAR结构的签名方式,keytool可识别
- v2/v3/v4(APK Signature Scheme):Android 7.0+引入的新签名方案,直接对APK文件的特定块签名,不依赖JAR结构。apksigner支持所有签名方案,但keytool仅能识别v1签名,因此会返回“Not a signed jar file”错误——这是正常现象,不代表APK签名无效。
2. signingInfo返回null的可能原因
- URI路径无效:如果
uri是FileProvider等Content Provider返回的内容URI,uri.path并非真实本地文件路径,getPackageArchiveInfo无法识别该路径导致解析失败。 - 文件读取权限不足:即便拥有设备所有者权限,若APK存储在外部存储且未获取
READ_EXTERNAL_STORAGE权限,或未通过ContentResolver正确读取文件,也会导致无法解析APK信息。 - API版本不兼容:
GET_SIGNING_CERTIFICATES是Android 9(API 28)才引入的常量,若运行在低于API 28的设备上,该参数无效,需改用旧版的GET_SIGNATURES。
正确获取APK签名证书的方法
方法1:修复路径与权限,使用PackageManager(API 28+)
先将Content URI转换为真实文件路径,确保应用能读取到APK文件:
// 转换Content URI为真实文件路径 fun getRealPathFromUri(context: Context, uri: Uri): String? { return when (uri.scheme) { "content" -> { val cursor = context.contentResolver.query(uri, arrayOf(MediaStore.Files.FileColumns.DATA), null, null, null) cursor?.use { if (it.moveToFirst()) { return it.getString(it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)) } } null } "file" -> uri.path else -> null } } // 获取APK签名信息 fun getApkSigningInfo(context: Context, apkUri: Uri): SigningInfo? { val apkPath = getRealPathFromUri(context, apkUri) ?: return null val packageInfo = context.packageManager.getPackageArchiveInfo( apkPath, PackageManager.GET_SIGNING_CERTIFICATES ) return packageInfo?.signingInfo }
调用后可提取签名证书:
val signingInfo = getApkSigningInfo(context, apkUri) signingInfo?.let { // 根据签名情况获取证书数组 val certificates = if (it.hasMultipleSigners()) { it.apkContentsSigners } else { it.signingCertificateHistory } // 遍历处理证书 certificates.forEach { cert -> val certHash = cert.toByteArray().joinToString("") { "%02x".format(it) } println("证书哈希值:$certHash") } }
方法2:兼容低版本API(API 19+)
若需要支持API 28以下的设备,可使用已过时但仍可用的GET_SIGNATURES:
fun getApkSignatures(context: Context, apkPath: String): Array<Signature>? { val packageInfo = context.packageManager.getPackageArchiveInfo( apkPath, PackageManager.GET_SIGNATURES ) return packageInfo?.signatures }
方法3:直接解析APK签名块(最可靠,不受API版本限制)
若上述方法仍有问题,可直接解析APK的签名相关条目(仅支持v1签名;若需支持v2/v3,可引入Android官方apksig库):
import java.io.File import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.util.zip.ZipFile fun getApkCertificates(apkFile: File): List<X509Certificate>? { return try { ZipFile(apkFile).use { zipFile -> val certFactory = CertificateFactory.getInstance("X.509") val entries = zipFile.entries() val certificates = mutableListOf<X509Certificate>() while (entries.hasMoreElements()) { val entry = entries.nextElement() // 筛选META-INF下的RSA签名文件 if (entry.name.startsWith("META-INF/") && entry.name.endsWith(".RSA")) { zipFile.getInputStream(entry).use { inputStream -> val cert = certFactory.generateCertificate(inputStream) as X509Certificate certificates.add(cert) } } } if (certificates.isEmpty()) null else certificates } } catch (e: Exception) { e.printStackTrace() null } }
内容的提问来源于stack exchange,提问作者Kugesh Rajasekaran
相关产品推荐
相关产品推荐

