Android如何通过包名编程获取已安装APK的SHA256摘要
通过包名获取已安装APK SHA256摘要的实现方法
核心逻辑:先通过包名定位到设备上对应APK的实际存储路径,再通过系统接口或官方签名工具提取签名证书的SHA256值,结果和手动执行apksigner verify --print-certs的输出完全一致。
方案1:Android设备端原生实现(在设备上运行的APP内调用)
该方案不需要依赖PC端工具,不需要root权限,普通应用即可调用。
- 先配置清单文件权限和包查询规则
Android 11及以上系统限制了应用对其他已安装APP的查询权限,需要在AndroidManifest.xml中添加如下配置:<!-- 可选:如果需要查询所有应用可保留该权限,仅查询指定包可省略 --> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Android 11+ 必须添加要查询的包声明 --> <queries> <package android:name="填入你要查询的目标包名" /> </queries> - 核心实现代码(Java为例)
import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.content.pm.SigningInfo; import android.os.Build; import java.security.MessageDigest; public class ApkSignUtil { public static String getInstalledApkSHA256(Context context, String packageName) throws Exception { PackageManager pm = context.getPackageManager(); // 校验应用是否安装,同时获取APK路径 ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); // 兼容不同版本系统获取签名数组 Signature[] signatures; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES); SigningInfo signingInfo = pkgInfo.signingInfo; // 自动兼容V1/V2/V3签名方案,取第一个签名证书即可,多证书场景可遍历数组 signatures = signingInfo.getApkContentsSigners(); } else { PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); signatures = pkgInfo.signatures; } if (signatures == null || signatures.length == 0) { throw new RuntimeException("无法获取目标应用的签名信息"); } byte[] certBytes = signatures[0].toByteArray(); // 计算证书的SHA256摘要 MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(certBytes); // 格式化为和apksigner输出一致的大写十六进制、冒号分隔格式 StringBuilder sb = new StringBuilder(); for (int i = 0; i < digest.length; i++) { String hex = Integer.toHexString(0xff & digest[i]); if (hex.length() == 1) sb.append('0'); sb.append(hex.toUpperCase()); if (i != digest.length - 1) sb.append(':'); } return sb.toString(); } }
方案2:PC端编程实现(通过ADB连接设备调用,无需在设备上安装额外APP)
该方案不需要给设备装应用,只要开启ADB调试即可,不需要root权限。
核心流程:先通过ADB命令查询对应包名的APK存储路径,再拉取APK到本地临时目录,调用官方apksigner工具提取SHA256即可,Python示例代码如下:
import subprocess import re import os def get_apk_sha256_by_pkg(pkg_name: str) -> str: # 查询设备上对应包名的APK路径 path_proc = subprocess.run( ["adb", "shell", "pm", "path", pkg_name], capture_output=True, text=True ) path_res = path_proc.stdout.strip() if not path_res.startswith("package:"): raise RuntimeError(f"设备上未找到包名为{pkg_name}的应用") device_apk_path = path_res.removeprefix("package:") # 拉取APK到本地临时目录 tmp_apk_path = f"/tmp/tmp_{pkg_name}.apk" subprocess.run( ["adb", "pull", device_apk_path, tmp_apk_path], capture_output=True, check=True ) try: # 调用apksigner获取证书信息 sign_proc = subprocess.run( ["apksigner", "verify", "--print-certs", tmp_apk_path], capture_output=True, text=True, check=True ) # 正则提取SHA256摘要 match = re.search(r"SHA-256 digest:\s*([0-9A-F:]+)", sign_proc.stdout) if not match: raise RuntimeError("提取SHA256摘要失败") return match.group(1) finally: # 清理临时文件 if os.path.exists(tmp_apk_path): os.remove(tmp_apk_path)
注意事项
- 采用Split APK分包机制的应用(按架构、资源拆分多个APK),不需要遍历所有分包文件,主包(base.apk)的签名和所有分包完全一致,直接读取主包签名即可。
- 不要自行实现APK签名块解析逻辑,系统PackageManager接口和官方apksigner工具已经兼容所有历史和新增的签名方案(V1/V2/V3/V4),自行解析容易出现版本兼容问题。
- *禁止使用仅读取JAR签名(V1)的老旧工具类计算摘要,在应用仅使用V2+签名的场景下会获取到错误结果。
内容的提问来源于stack exchange,提问作者user3797829
相关产品推荐
相关产品推荐

