SigningDetails 签名信息
SigningDetails 不是一个证书数组包装类。它同时描述当前签名者、签名方案版本、签名方案 minor version、公钥集合和历史签名证书。安装更新、sharedUserId、签名权限和回滚分别请求不同 capability,因此“证书相同”只是最简单的一种结果。
1. 数据结构
源码文件:frameworks/base/core/java/android/content/pm/SigningDetails.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. 方案与能力
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 与不变量
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. 证书与公钥
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 关系
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 检查
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
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 约束
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
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 合并
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. 排查清单
- 先确认
SigningDetails是否为UNKNOWN,未知状态不会被当作匹配。 - 区分 exact signer、ancestor/self 和 capability match 三种结果。
- 更新失败检查
INSTALLED_DATA,回滚失败检查ROLLBACK。 - sharedUserId 失败检查
SHARED_USER_ID和 join type。 - signature 权限失败检查
PERMISSIONcapability,而不是只比较 APK 当前证书。 - 多 signer 包不能套用单 signer rotation 逻辑。
- v3.2 hybrid signer 需要同时满足 classical 与 primary key 约束。
12. 源码阅读路线
SigningDetails字段、scheme version 和 capability 常量。hasAncestorOrSelf/hasAncestor:理解 lineage 方向。checkCapability/hasCommonSignerWithCapability:理解不同安全动作的能力位。InstallPackageHelper:更新、回滚和系统包签名消费。PackageManagerServiceUtils.canJoinSharedUserId:shared UID 的组合约束。AppIdPermissionPolicy.shouldGrantPermissionBySignature:签名权限的最终使用。
SigningDetails 建模的是“签名身份及其历史授权能力”。Android 17 用它同时支撑更新、回滚、shared UID 和 signature permission,但每条链请求的 capability 不同;这正是阅读签名源码时最需要保持的边界。
