签名变更处理
签名变更不是把旧证书替换成新证书后继续比较。APK Signature Scheme v3 把旧 signer 组成有序 lineage,并由每个历史证书的 capability 决定它还能否保留安装数据、shared UID、签名权限或回滚能力。Android 17 的安装代码把这些能力分别用于更新、回滚、系统包和 shared UID 判断。
1. 变更的三种关系
| 关系 | API | 典型用途 |
|---|---|---|
| 完全相同 | signaturesMatchExactly | 多 signer、无变更 |
| lineage 包含 | hasAncestorOrSelf | 判断旧 signer 是否仍在历史链 |
| 能力授权 | checkCapability | 更新、权限、shared UID、回滚 |
hasAncestorOrSelf 只回答“是否属于同一信任链”,不回答是否授予某项能力;能力必须再由 checkCapability(flags) 验证。
2. PoR 与 lineage
v3 验证器把 Proof-of-Rotation 中的历史证书转换为 SigningDetails.mPastSigningCertificates。数组按旧到新的顺序保存,最后一项通常是当前 signer;每个 Signature 的 flags 保存历史 signer 被授予的能力。
3. lineage 方向
源码文件:frameworks/base/core/java/android/content/pm/SigningDetails.java
public boolean hasAncestorOrSelf(
@NonNull SigningDetails oldDetails) {
if (this == UNKNOWN || oldDetails == UNKNOWN) {
return false;
}
if (oldDetails.mSignatures.length > 1) {
return signaturesMatchExactly(oldDetails);
}
return hasCertificate(oldDetails.mSignatures[0]);
}
public boolean hasAncestor(
@NonNull SigningDetails oldDetails) {
if (this == UNKNOWN || oldDetails == UNKNOWN) {
return false;
}
if (hasPastSigningCertificates()
&& oldDetails.mSignatures.length == 1) {
for (int i = 0;
i < mPastSigningCertificates.length - 1; i++) {
if (mPastSigningCertificates[i].equals(
oldDetails.mSignatures[0])) {
return true;
}
}
}
return false;
}当前对象调用 hasAncestor(oldDetails),表示当前 signer 的 lineage 中出现旧 signer,且不是当前 signer 本身。多 signer 包没有 rotation,退化为 exact match。
4. capability 检查
public boolean checkCapability(
@NonNull SigningDetails oldDetails,
@CertCapabilities int flags) {
if (this == UNKNOWN || oldDetails == UNKNOWN) {
return false;
}
if (oldDetails.mSignatures.length > 1) {
return signaturesMatchExactly(oldDetails);
}
boolean matchFound = hasCertificate(
oldDetails.mSignatures[0], flags);
if (!android.security.Flags.apkPqcHybridSigning()
|| !matchFound) {
return matchFound;
}
if (isV32Hybrid() || oldDetails.isV32Hybrid()) {
return checkV32HybridCapability(oldDetails, flags);
}
return true;
}单 signer 场景先查旧 signer 是否在当前 lineage,再检查该历史证书的 flags。多 signer 必须完全相同;不会因为某个 signer 恰好出现在 lineage 中就放行。
5. 更新判断
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
SigningDetails parsedPkgSigningDetails =
parsedPackage.getSigningDetails();
SigningDetails oldPkgSigningDetails =
oldPackageState.getSigningDetails();
if (!parsedPkgSigningDetails.checkCapability(
oldPkgSigningDetails,
SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !oldPkgSigningDetails.checkCapability(
parsedPkgSigningDetails,
SigningDetails.CertCapabilities.ROLLBACK)) {
if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
parsedPkgSigningDetails)) {
throw new PrepareFailure(
INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName);
}
}正常更新看新包是否授予旧 signer INSTALLED_DATA;回滚看旧 signer 是否授予新 signer ROLLBACK。两者都失败时,只有回滚且旧 lineage 包含待回滚 signer 才能继续。
6. 能力撤销
历史证书可以在新版本的 lineage 中保留证书,但清除某个 capability。这样“同一 lineage”不等于“所有旧行为继续有效”。例如旧 signer 仍在链中,但没有 PERMISSION,新包就不能凭该旧 signer 获取签名权限;没有 SHARED_USER_ID 也不能加入 shared UID。
7. shared UID 变更
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
boolean capabilityGranted =
packageSigningDetails.checkCapability(
sharedUserSigningDetails,
SigningDetails.CertCapabilities.SHARED_USER_ID)
|| sharedUserSigningDetails.checkCapability(
packageSigningDetails,
SigningDetails.CertCapabilities.SHARED_USER_ID);
if (capabilityGranted
&& joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL) {
return true;
}
if (!capabilityGranted
&& sharedUserSigningDetails.hasAncestor(packageSigningDetails)) {
return joinType == SHARED_USER_ID_JOIN_TYPE_SYSTEM;
}
if (!capabilityGranted
&& packageSigningDetails.hasAncestor(sharedUserSigningDetails)) {
return joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL;
}shared UID 的 install、update、system join 不是同一权限级别。capability 被撤销时,系统 join 或更新可能仍允许迁移,普通新安装则被拒绝,避免新包借旧 signer 进入已有 shared UID 组。
8. 系统包检查
private static boolean matchSignatureInSystem(
String packageName, SigningDetails signingDetails,
PackageSetting disabledPkgSetting) {
return signingDetails.checkCapability(
disabledPkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| disabledPkgSetting.getSigningDetails().checkCapability(
signingDetails,
SigningDetails.CertCapabilities.ROLLBACK);
}系统应用更新同时要通过 data 包和 disabled system package 的签名能力检查。只与 /data 旧包兼容而与出厂包不兼容,仍会被拒绝。
9. v3.2 hybrid
Android 17 的 checkCapability 在 PQC hybrid flag 开启时会额外调用 checkV32HybridCapability。如果一个 hybrid 身份只复用了 primary 或 classical key,而不是成对出现,能力检查失败并记录签名策略指标,防止单钥复用绕过 lineage 信任。
if (oldDetails.isV32Hybrid()) {
if (!hasCertificate(
oldDetails.getV32ClassicalHybridSigner(), flags)) {
return false;
}
}这条约束只在 hybrid signer 场景触发;普通 v3 rotation 仍按历史证书和 flags 判断。
10. 排查顺序
- 先确认新旧
SigningDetails是否为 UNKNOWN、单 signer 还是多 signer。 - 打印 lineage 顺序,确认旧 signer 是当前 signer、ancestor 还是完全不相关。
- 按场景选择 capability:更新看
INSTALLED_DATA,回滚看ROLLBACK,shared UID 看SHARED_USER_ID,签名权限看PERMISSION。 - 检查 capability 是否被新 lineage 撤销。
- 系统应用更新额外检查 disabled system package。
- shared UID 失败时区分 install、update、system join type。
- hybrid signer 失败时检查 primary/classical 两把 key 是否成对存在。
11. 源码阅读路线
SigningDetails.hasAncestorOrSelf/hasAncestor:确认 lineage 方向。SigningDetails.checkCapability:确认历史证书 flags 和 hybrid 约束。InstallPackageHelper更新/回滚分支:观察 capability 消费。PackageManagerServiceUtils.verifySignatures:观察 compat/recover 和系统包检查。canJoinSharedUserId:理解 shared UID 在不同 join type 下的差异。
签名轮换的安全边界是“新 signer 通过旧 signer 的背书获得有限能力”,而不是永久继承旧证书的全部信任。Android 17 将 lineage、capability、更新类型和包来源组合起来,任何一项不满足都可能改变安装结果。
