androd签名apk笔记
Android 应用签名机制 知识思维导图,覆盖了 7 大主题分支:
- 为什么App需要系统签名 — 身份认证、完整性防篡改、升级管控、数据共享、系统级权限
- 网上下载的App能安装的原因 — 未知来源权限、签名有效性、绕过纯净模式、APK完整性
- 如何判断APK是否可信 — 权限检查、签名验证、多引擎扫描、官方渠道下载
- 开发者如何给应用签名 — 生成证书→签名→Zipalign→验证
- 系统签名是否需要源码 — 源码编译方式、手动提取密钥重签名、安装限制
- 权限授权流程 — Shared User ID声明、特权白名单配置、敏感运行时权限处理
- 签名版本演进 (v1/v2/v3) — 各版本保护范围与兼容性对比
讲讲开发者是怎么给应用签名的
开发者给应用签名的过程,实际上是一个从“生成身份证书”到“验证并优化安装包”的标准化流程。通常可以分为以下四个核心步骤:
第一步:生成数字证书(密钥库)
开发者首先需要创建一个包含私钥的数字证书(通常以 .keystore 或 .jks 为后缀)。这一步相当于开发者为自己打造一枚“专属印章”。在实际操作中,开发者会使用 Java 开发工具包(JDK)中的 keytool 工具,通过命令行指定算法(如 RSA)、密钥大小和有效期等参数来生成这个证书文件。
第二步:使用证书为应用签名
在编译代码和资源文件,生成未签名的 APK 文件后,开发者需要为应用“盖章”。目前主流的做法是在 Android Studio 的 build.gradle 文件中配置签名信息(包括证书路径、密码和别名等),构建系统会在打包时自动完成签名。对于需要手动签名的场景,开发者会使用 apksigner 或 jarsigner 命令行工具,将数字证书与 APK 文件结合,生成带有签名的 APK。
第三步:对齐优化(Zipalign)
签名完成后,开发者通常会使用 Android SDK 提供的 zipalign 工具对 APK 进行优化。这个步骤会对 APK 内部未压缩的数据进行 4 字节对齐,从而减少运行时的内存消耗,让应用在安卓设备上运行得更快、更高效。
第四步:验证签名
最后,开发者必须对生成的 APK 进行签名验证,以确保签名过程没有出错。同样可以使用 apksigner verify 或 keytool 命令来查看 APK 的证书信息、签名版本(如 v1、v2、v3)以及是否有效。
目前,为了兼顾不同版本 Android 系统的安全性和兼容性,开发者在配置时通常会同时启用 v1、v2 甚至 v3 签名方案。
关于签名机制,其实还有 v1、v2、v3 等不同版本的演进,它们在保护范围和安全性上差别很大。需要我帮你梳理一下这几个版本的区别吗?
系统签名是要用安卓源码进行?
是的,给应用进行系统签名(即使用 platform 平台签名)必须依赖 Android 系统源码环境。
这主要是因为系统签名所使用的核心密钥文件(如 platform.pk8 和 platform.x509.pem)以及专用的签名工具(如 signapk.jar)都只存在于 Android 源码的编译目录中。
在实际开发中,开发者通常通过以下两种方式来利用源码环境完成系统签名:
方式一:在源码环境中直接编译(推荐)
开发者将应用的源码拷贝到 Android 源码的 packages/apps/ 目录下,并在该应用的 Android.mk 配置文件中添加 LOCAL_CERTIFICATE := platform 这一行。随后,使用 mm 命令进行编译,构建系统会自动使用平台密钥为该应用签名,生成的 APK 即为系统签名应用。
方式二:手动提取密钥进行重签名
如果不想在庞大的源码环境中编译整个应用,开发者也可以先通过 Android Studio 等工具正常编译出普通的 APK 文件。然后,从 Android 源码的 build/target/product/security/ 目录下提取出 platform.pk8 和 platform.x509.pem 密钥文件,并结合源码提供的 signapk.jar 工具,通过执行命令(如 java -jar signapk.jar platform.x509.pem platform.pk8 input.apk output.apk)来手动给 APK 重新签名。
重要安全限制
系统签名密钥是高度敏感的,必须在 Android 源码环境下妥善保管。使用这种方式生成的系统签名应用,只能安装在由同一套系统源码编译出的 Android 系统上。如果将其安装到其他厂商(如小米、华为等)或未经授权的系统中,系统会因为签名证书不匹配而拒绝安装,这是 Android 保护系统底层安全的重要机制。
系统签名应用装到自编译系统后,想直接调用底层硬件权限(比如相机、传感器)通常还需要额外配置,要不要说说这块的权限授权流程?
好的,说说这块的权限授权流程
系统签名应用(Privileged System App)获取底层硬件或系统级权限的流程,与普通第三方应用有很大不同。它不再依赖用户在界面上的弹窗授权,而是依赖于 Android 系统底层的白名单机制和默认授权策略。
具体来说,权限授权流程分为以下三个核心步骤:
声明系统共享用户 ID (Shared User ID)
首先,开发者需要在应用的 AndroidManifest.xml 文件中,将应用配置为运行在系统进程中。这通常通过添加 android:sharedUserId=“android.uid.system” 属性来实现。这一步是应用获取系统级身份的基础。配置特权权限白名单 (Priv-app Permissions Whitelist)
对于需要 signature(签名级)或 privileged(特权级)的高危系统权限(例如静默安装应用的 INSTALL_PACKAGES、修改安全设置的 WRITE_SECURE_SETTINGS 等),仅仅将应用放在 /system/priv-app 目录下并使用平台签名是不够的。
从 Android 10 开始,系统引入了“强制特许权限白名单”机制。开发者必须在源码的特定配置文件(如 privapp-permissions-platform.xml)中,显式地声明并放行该应用所需的所有特权权限。如果未在此白名单中配置,应用即使拥有系统签名,在启动时也会因权限校验失败而崩溃。处理敏感运行时权限 (Dangerous Permissions)
对于涉及用户隐私的敏感硬件权限(如相机 CAMERA、麦克风、位置等),Android 出于隐私保护,不会在安装时自动授予,即便是系统签名应用也不例外。
这类权限需要通过修改系统源码中的 DefaultPermissionGrantPolicy.java 文件来硬编码授权。在系统首次启动或恢复出厂设置时,系统会执行该文件中的逻辑,将指定的敏感权限默认授予核心的系统应用。
总结:
系统签名应用调用底层权限,本质上是将原本由“用户手动授权”的动作,提前到了“系统源码编译与配置”阶段。开发者必须在源码层面完成身份绑定、特权白名单放行以及敏感权限的默认授予策略,才能确保应用顺利调用底层能力。
在实际开发中,配置 privapp-permissions-platform.xml 时很容易因为格式或路径写错导致设备无法开机(Bootloop)。需要我帮你梳理一下这个白名单文件的正确配置模板和常见避坑指南吗?
为什么还要系统签名
这是一个很好的追问。在前面的对话中我们已经聊了"App 为什么需要签名",但"系统签名"和"普通开发者签名"是两回事。简单来说,系统签名是普通签名做不到的事情,才需要它。具体原因可以归纳为以下几点:
- 普通签名拿不到的系统级权限
Android 的权限体系分为四个等级:normal(普通)、dangerous(危险)、signature(签名级)、signatureOrSystem(签名或系统)。前两种权限,普通签名应用可以申请,危险权限还需要用户弹窗同意。但像 INSTALL_PACKAGES(静默安装)、WRITE_SECURE_SETTINGS(修改安全设置)、ACCESS_FINE_LOCATION 在系统上下文中的权限等,都标记为 signatureOrSystem 或 system 级别,只有使用 platform 系统签名的应用才能获取。普通签名应用即使声明了这些权限,系统也会直接拒绝授予。
- 以系统进程身份运行
通过系统签名,应用可以设置 sharedUserId=“android.uid.system”,从而运行在系统进程中,拥有和 SystemServer 相同的 UID。这意味着应用可以:
- 直接访问 /system、/data/system 等系统目录
- 调用 Framework 层的隐藏 API(不需要反射绕过)
- 与其他系统组件(如 ActivityManagerService、PackageManagerService)直接通信
普通签名应用跑在独立的沙箱中,UID 是随机分配的(u0_aXX),这些能力完全不可达。
- 绕过安装来源验证和纯净模式
系统签名应用被系统视为"可信组件",安装时不会触发未知来源警告,也不会被华为、小米等厂商的"纯净模式"拦截。这对于企业定制 ROM、工业控制设备、物联网终端等场景非常关键——这些设备不需要应用商店审核机制,但需要预装的应用拥有完整系统权限。
- 开机自启动且不被系统杀死
普通签名应用在后台很容易被 Android 的内存回收机制(Low Memory Killer)杀死,而且开机后需要用户手动打开才能运行。系统签名应用可以被注册为系统服务或广播接收器,随系统开机自动启动,并且不会被普通的"清理后台"操作终止。这在车载系统、POS 机、智能音箱等需要 7×24 小时运行的场景中是刚需。
- 修改系统全局设置
普通签名应用无法修改 settings put global/secure 中的受保护项。而系统签名应用可以:
- 关闭或绕过屏幕锁定(LockSettings)
- 修改默认输入法、默认浏览器等全局偏好
- 设置 adb 调试权限、USB 调试模式
- 控制飞行模式、Wi-Fi 开关等
- 系统级数据共享
多个使用相同系统签名的应用可以被系统视为"同一程序",共享 UID、共享 /data/data/ 目录下的数据文件夹、在同一进程中运行。这对于将系统功能拆分为多个 APK(如 Google 的 GMS 套件)非常有用,普通签名做不到这一点。
总结一句话: 系统签名的本质是让应用获得"半个系统"的身份,普通签名只是证明"这个应用来自谁",而系统签名证明的是"这个应用就是系统的一部分"。两者解决的安全层级完全不同。
如果想进一步了解,我可以帮你梳理一下:
- 系统签名在 Android 10+ 版本中的安全收紧趋势(比如强制白名单、v4 签名方案)
- 或者在实际定制 ROM(如 LineageOS)中如何给第三方应用做系统签名的完整操作指南
签名问题
在一个项目中使用的apk以android.mk的方式编译到系统,是需要签名,如果想要安装 到目标机器上,需要在目标机器系统的sdk中重新签名,即重新编译,再安装.apk即可
这里按上面的做法后,可以在目标机器的安装 打开
Android.mk采用的模块如下
#LOCAL_PATH:=$(call my-dir)#include$(CLEAR_VARS)#LOCAL_MODULE:=KehwinBLE#LOCAL_MODULE_TAGS:=optional#LOCAL_PRIVILEGED_MODULE:=true#LOCAL_SRC_FILES:=./KehwinBLE.apk#LOCAL_MODULE_CLASS:=APPS#LOCAL_MODULE_SUFFIX:=$(COMMON_ANDROID_PACKAGE_SUFFIX)#LOCAL_CERTIFICATE:=platform#include$(BUILD_PREBUILT)LOCAL_PATH:=$(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE:=AutoUpdate LOCAL_SRC_FILES:=AutoUpdate.apk LOCAL_MODULE_CLASS:=APPS LOCAL_MODULE_SUFFIX:=.apk LOCAL_BUILT_MODULE_STEM:=package.apk LOCAL_CERTIFICATE:=platform include $(BUILD_PREBUILT)
android 9
LOCAL_PATH:=$(my-dir)APK_NAME_FULL:=$(shell cd $(LOCAL_PATH);ls-A|grep apk)APK_NAME:=$(shell echo $(APK_NAME_FULL)|sed's/.apk//g')$(warning--------------FullName=$(APK_NAME_FULL)----Name=$(APK_NAME))define get-all-libraries-module-name-in-subdirs $(sort $(shell cd $(LOCAL_PATH);rm-rf lib>/dev/null;unzip $(APK_NAME_FULL)'lib/*.so'-d.>/dev/null;find-L $(1)-name"*.so"))endef ALL_LIBRARIES_MODULE_NAME:=$(call get-all-libraries-module-name-in-subdirs,lib/armeabi-v7a)$(warning--------------ALL_LIBRARIES_MODULE_NAME:$(ALL_LIBRARIES_MODULE_NAME))include $(CLEAR_VARS)LOCAL_MODULE:=BQB_V1.1.0.31_realtek LOCAL_MODULE_CLASS:=APPS LOCAL_BUILT_MODULE_STEM:=package.apk LOCAL_CERTIFICATE:=platform LOCAL_PRIVILEGED_MODULE:=trueLOCAL_SRC_FILES:=$(LOCAL_MODULE).apk LOCAL_MODULE_SUFFIX:=$(COMMON_ANDROID_PACKAGE_SUFFIX)LOCAL_MULTILIB:=32LOCAL_PREBUILT_JNI_LIBS:=$(ALL_LIBRARIES_MODULE_NAME)include $(BUILD_PREBUILT)