Skip to content

SigningDetails 签名信息

解析当前证书、签名 lineage、能力标志和签名比较。

AndroidPMS签名SigningDetails

SigningDetails 签名信息 ​

SigningDetails 不是一个证书数组包装类。它同时描述当前签名者、签名方案版本、签名方案 minor version、公钥集合和历史签名证书。安装更新、sharedUserId、签名权限和回滚分别请求不同 capability,因此“证书相同”只是最简单的一种结果。

1. 数据结构 ​

源码文件:frameworks/base/core/java/android/content/pm/SigningDetails.java

java
private final @Nullable Signature[] mSignatures;
private final @SignatureSchemeVersion int mSignatureSchemeVersion;
private final int mSignatureSchemeMinorVersion;
private final @Nullable ArraySet<PublicKey> mPublicKeys;
private final @Nullable Signature[] mPastSigningCertificates;

public static final SigningDetails UNKNOWN = new SigningDetails(
        null, SignatureSchemeVersion.UNKNOWN,
        /* keys */ null, /* pastSigningCertificates */ null);

UNKNOWN 是显式占位状态,避免用 null 表示“尚未解析”。mSignatures 是当前 signer;mPastSigningCertificates 保存 v3 rotation lineage;mPublicKeys 是从当前证书提取的公钥集合,用于快速密钥比较。

2. 方案与能力 ​

java
public @interface SignatureSchemeVersion {
    int UNKNOWN = 0;
    int JAR = 1;
    int SIGNING_BLOCK_V2 = 2;
    int SIGNING_BLOCK_V3 = 3;
    int SIGNING_BLOCK_V4 = 4;
}

public @interface CertCapabilities {
    int INSTALLED_DATA = 1;
    int SHARED_USER_ID = 2;
    int PERMISSION = 4;
    int ROLLBACK = 8;
    int AUTH = 16;
}

capability 是历史证书上的位掩码:新签名是否继续信任旧安装数据、shared UID、signature permission、rollback 或 auth,分别由不同位表达。它们不是签名方案版本的别名。

3. Builder 与不变量 ​

java
public static class Builder {
    private @NonNull Signature[] mSignatures;
    private @SignatureSchemeVersion int mSignatureSchemeVersion =
            SignatureSchemeVersion.UNKNOWN;
    private @SignatureSchemeMinorVersion int mSignatureSchemeMinorVersion =
            SignatureSchemeMinorVersion.MINOR_VERSION_DEFAULT;
    private @Nullable Signature[] mPastSigningCertificates;

    private void checkInvariants() {
        if (mSignatures == null) {
            throw new IllegalStateException(
                    "SigningDetails requires the current signing certificates.");
        }
    }

    public SigningDetails build() throws CertificateException {
        checkInvariants();
        return new SigningDetails(
                mSignatures, mSignatureSchemeVersion,
                mSignatureSchemeMinorVersion,
                mPastSigningCertificates);
    }
}

Builder 至少要求当前签名证书存在,历史证书可以为空。构建后的对象保存不可变语义;公钥集合通过 toSigningKeys 从证书解析,而不是由调用方随意传入。

4. 证书与公钥 ​

java
public static ArraySet<PublicKey> toSigningKeys(
        @NonNull Signature[] signatures)
        throws CertificateException {
    final ArraySet<PublicKey> keys =
            new ArraySet<>(signatures.length);
    for (int i = 0; i < signatures.length; i++) {
        keys.add(signatures[i].getPublicKey());
    }
    return keys;
}

public boolean signaturesMatchExactly(
        @NonNull SigningDetails other) {
    return Signature.areExactMatch(this, other);
}

公钥集合用于 key-level 判断,signaturesMatchExactly 则要求 signer 数量和顺序都一致。多 signer 包不能用单 signer lineage 的直觉处理;源码在 ancestor/capability 方法中会对多 signer 直接走 exact match。

5. ancestor 关系 ​

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;
}

hasAncestorOrSelf 允许“当前签名就是旧签名”或“旧签名位于当前 lineage”;hasAncestor 排除 self,只判断是否真的发生了 rotation。多 signer 包不支持 rotation,因此只能 exact match。

6. capability 检查 ​

java
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;
}

调用方传入 capability,方法先找旧 signer 是否在当前 lineage,再检查该历史证书的 flags。多 signer 直接 exact match,视为所有 capability 满足。Android 17 还对 v3.2 hybrid signer 做双钥复用检查,不能只命中其中一个 key。

7. 更新消费 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java

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 对旧 signer 具备 INSTALLED_DATA;回滚允许旧 signer 对新 signer 具备 ROLLBACK,并且额外检查 lineage。这里正是“证书不同但更新仍可接受”的源码依据。

8. shared UID 约束 ​

java
SigningDetails sharedUserSigningDetails =
        sharedUserSetting.getSigningDetails();
boolean capabilityGranted =
        packageSigningDetails.checkCapability(
                sharedUserSigningDetails,
                SHARED_USER_ID)
        || sharedUserSigningDetails.checkCapability(
                packageSigningDetails, SHARED_USER_ID);

shared UID 加入判断使用 SHARED_USER_ID capability,而不是只比较当前证书。安装、更新、system join 的 join type 还会改变 lineage 被撤销时是否允许加入;因此 sharedUserId 是 signer、capability 和 join type 的组合判断。

9. 签名权限能力 ​

源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt

kotlin
val hasCommonSigner =
    sourceSigningDetails?.hasCommonSignerWithCapability(
        packageSigningDetails,
        SigningDetails.CertCapabilities.PERMISSION,
    ) == true ||
        packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) ||
        platformSigningDetails.checkCapability(
            packageSigningDetails,
            SigningDetails.CertCapabilities.PERMISSION,
        )

签名权限使用 PERMISSION capability。更新数据的 INSTALLED_DATA、回滚的 ROLLBACK 和 shared UID 的 SHARED_USER_ID 不能互换;同一个 lineage 对不同安全动作可以授予不同能力。

10. lineage 合并 ​

java
public @NonNull SigningDetails mergeLineageWith(
        @NonNull SigningDetails otherSigningDetails) {
    return mergeLineageWith(otherSigningDetails,
            CapabilityMergeRule.MERGE_OTHER_CAPABILITY);
}

public @interface CapabilityMergeRule {
    int MERGE_SELF_CAPABILITY = 0;
    int MERGE_OTHER_CAPABILITY = 1;
    int MERGE_RESTRICTED_CAPABILITY = 2;
}

多个来源需要合并 lineage 时,能力冲突不能随意覆盖:可以取当前对象、另一对象或更严格能力。MERGE_RESTRICTED_CAPABILITY 用于降低合并后的信任范围。

11. 排查清单 ​

  1. 先确认 SigningDetails 是否为 UNKNOWN,未知状态不会被当作匹配。
  2. 区分 exact signer、ancestor/self 和 capability match 三种结果。
  3. 更新失败检查 INSTALLED_DATA,回滚失败检查 ROLLBACK。
  4. sharedUserId 失败检查 SHARED_USER_ID 和 join type。
  5. signature 权限失败检查 PERMISSION capability,而不是只比较 APK 当前证书。
  6. 多 signer 包不能套用单 signer rotation 逻辑。
  7. v3.2 hybrid signer 需要同时满足 classical 与 primary key 约束。

12. 源码阅读路线 ​

  1. SigningDetails 字段、scheme version 和 capability 常量。
  2. hasAncestorOrSelf / hasAncestor:理解 lineage 方向。
  3. checkCapability / hasCommonSignerWithCapability:理解不同安全动作的能力位。
  4. InstallPackageHelper:更新、回滚和系统包签名消费。
  5. PackageManagerServiceUtils.canJoinSharedUserId:shared UID 的组合约束。
  6. AppIdPermissionPolicy.shouldGrantPermissionBySignature:签名权限的最终使用。

SigningDetails 建模的是“签名身份及其历史授权能力”。Android 17 用它同时支撑更新、回滚、shared UID 和 signature permission,但每条链请求的 capability 不同;这正是阅读签名源码时最需要保持的边界。