Skip to content

APK 签名方案

解析 APK 签名方案选择、降级边界和安装阶段校验。

AndroidPMSAPK签名

APK 签名方案 ​

APK 签名验证不是“依次尝试 v1、v2、v3、v4”这么简单。Android 17 的 ApkSignatureVerifier 根据调用方要求的最低 scheme 版本选择路径:优先尝试 v4,失败后在允许范围内降到 v3、v2、v1;安装流程随后还会把新包签名与已安装包、共享 UID、回滚能力和目标 SDK 的最低方案要求进行比较。

1. 方案职责 ​

方案数据位置主要保护
v1 JARMETA-INF ZIP 条目逐条目摘要
v2APK Signing BlockAPK 内容与 ZIP 结构
v3/v3.1APK Signing Blockv2 能力加密钥轮换/版本属性
v4外部 .idsigfs-verity/增量安装辅助

v4 不单独替代 APK 内 v2/v3 证书;Android 17 的 v4 验证路径会读取 v2 签名信息,再验证 v4 的 fs-verity 数据。

2. 统一验证入口 ​

源码文件:frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java

java
public static ParseResult<SigningDetails> verify(
        ParseInput input, String apkPath,
        @SignatureSchemeVersion int minSignatureSchemeVersion) {
    return verifySignatures(
            input, apkPath, minSignatureSchemeVersion,
            true /* verifyFull */);
}

public static ParseResult<SigningDetails> unsafeGetCertsWithoutVerification(
        ParseInput input, String apkPath,
        int minSignatureSchemeVersion) {
    return verifySignatures(
            input, apkPath, minSignatureSchemeVersion,
            false /* verifyFull */);
}

正常入口 verifyFull=true 校验 APK 内容;unsafeGetCertsWithoutVerification 只为需要提取证书的受控场景提供较弱路径。两者都返回 SigningDetails,而不是只返回一个证书数组。

3. v4 优先 ​

java
public static ParseResult<SigningDetailsWithDigests> verifySignaturesInternal(
        ParseInput input, String apkPath,
        @SignatureSchemeVersion int minSignatureSchemeVersion,
        boolean verifyFull) {
    if (minSignatureSchemeVersion
            > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
        return input.error(
                INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                "No signature found in package of version "
                        + minSignatureSchemeVersion
                        + " or newer for package " + apkPath);
    }
    try {
        return verifyV4Signature(
                input, apkPath, minSignatureSchemeVersion, verifyFull);
    } catch (SignatureNotFoundException e) {
        if (minSignatureSchemeVersion
                >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
            return input.error(
                    INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No APK Signature Scheme v4 signature in package "
                            + apkPath, e);
        }
    }

调用方要求 v4 时,找不到 v4 直接报错;只有最低版本低于 v4,才允许继续降级。SignatureNotFoundException 表示方案不存在,不等同于签名数据损坏;损坏通常由 verifier 返回解析错误。

4. v3、v2、v1 降级 ​

java
if (minSignatureSchemeVersion
        > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
    return input.error(
            INSTALL_PARSE_FAILED_NO_CERTIFICATES,
            "No signature found in package of version "
                    + minSignatureSchemeVersion + " or newer");
}
return verifyV3AndBelowSignatures(
        input, apkPath, minSignatureSchemeVersion, verifyFull);

源码文件:frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java

java
private static ParseResult<SigningDetailsWithDigests>
verifyV3AndBelowSignatures(
        ParseInput input, String apkPath,
        @SignatureSchemeVersion int minSignatureSchemeVersion,
        boolean verifyFull) {
    try {
        return verifyV3Signature(input, apkPath, verifyFull);
    } catch (SignatureNotFoundException e) {
        if (minSignatureSchemeVersion
                >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
            return input.error(
                    INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No APK Signature Scheme v3 signature in package "
                            + apkPath, e);
        }
    }
    if (minSignatureSchemeVersion
            > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
        return input.error(
                INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                "No signature found in package of version "
                        + minSignatureSchemeVersion + " or newer");
    }
    try {
        return verifyV2Signature(input, apkPath, verifyFull);
    } catch (SignatureNotFoundException e) {
        if (minSignatureSchemeVersion
                >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
            return input.error(
                    INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No APK Signature Scheme v2 signature in package "
                            + apkPath, e);
        }
    }
    if (minSignatureSchemeVersion
            > SignatureSchemeVersion.JAR) {
        return input.error(
                INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                "No signature found in package of version "
                        + minSignatureSchemeVersion + " or newer");
    }
    return verifyV1Signature(input, apkPath, verifyFull);
}

降级是“最低版本约束下的降级”:要求 v3 的调用不会接受只有 v2 的 APK;允许 v1 的调用才会最终尝试 JAR。这个判断由 minSignatureSchemeVersion 控制,不由设备当前 Android 版本自动放宽。

5. 安装时读取签名 ​

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

java
if (request.getSigningDetails() != SigningDetails.UNKNOWN) {
    parsedPackage.setSigningDetails(
            request.getSigningDetails());
} else {
    final ParseTypeImpl input =
            ParseTypeImpl.forDefaultParsing();
    final ParseResult<SigningDetails> result =
            ParsingPackageUtils.getSigningDetails(
                    input, parsedPackage,
                    false /* skipVerify */);
    if (result.isError()) {
        throw new PrepareFailure(
                "Failed collect during installPackageLI",
                result.getException());
    }
    parsedPackage.setSigningDetails(result.getResult());
}

安装会话如果已提供签名详情则复用,否则从 APK 解析。skipVerify=false 表示收集阶段仍要求正常签名验证;结果写入 parsedPackage,后续更新兼容性检查消费该对象。

6. Instant App 要求 ​

java
if (instantApp && parsedPackage.getSigningDetails()
        .getSignatureSchemeVersion()
        < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
    Slog.w(TAG, "Instant app package "
            + parsedPackage.getPackageName()
            + " is not signed with at least APK Signature Scheme v2");
    throw new PrepareFailure(
            INSTALL_FAILED_SESSION_INVALID,
            "Instant app package must be signed with APK Signature Scheme v2 or greater");
}

Instant App 至少要求 v2。即使普通 APK 可以因兼容性降级到 v1,Instant App 安装路径也会在签名方案版本不足时直接失败。

7. 更新包的签名能力 ​

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

java
SigningDetails parsed = parsedPackage.getSigningDetails();
SigningDetails old = oldPackageState.getSigningDetails();
if (!parsed.checkCapability(
        old, SigningDetails.CertCapabilities.INSTALLED_DATA)
        && !old.checkCapability(
                parsed, SigningDetails.CertCapabilities.ROLLBACK)) {
    if (!isRollback || !old.hasAncestorOrSelf(parsed)) {
        throw new PrepareFailure(
                INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                "New package has a different signature: "
                        + pkgName);
    }
}

更新不是简单比较当前证书字节。INSTALLED_DATA 能力允许新签名继续使用旧安装数据;ROLLBACK 能力允许回滚路径使用另一方向的签名能力。两者都不满足时,普通更新失败;回滚还要满足旧签名 lineage 条件。

8. 签名权限能力 ​

源码文件:frameworks/base/services/core/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。同一套 SigningDetails 支持不同安全目的,不能把“能更新旧包”推导成“能获得对方 signature 权限”。

9. v4 与增量安装 ​

v4 验证器的输出包含签名者和 content digests,供增量安装/fs-verity 使用;APK 本身仍需要可验证的 v2/v3 签名信息。安装会话还会验证 .idsig 或 digest signature 文件,签名不一致时抛出 INSTALL_FAILED_INVALID_APK。

java
Certificate[] ignored = ApkChecksums.verifySignature(
        checksums, signature);

这里校验的是 checksum 文件签名,不是替代 APK signer。排查增量安装失败时,应分别检查 APK scheme、.idsig/digest 文件和安装会话保存的 SigningDetails。

10. 排查顺序 ​

  1. 先确认调用方要求的最低签名方案,避免把“方案不存在”误认为签名损坏。
  2. 检查 v4 外部文件和 APK 内 v2/v3 是否同时存在且一致。
  3. 更新失败时比较 INSTALLED_DATA、ROLLBACK capability,而不是只比较当前证书文本。
  4. Instant App 安装至少检查 v2。
  5. signature 权限问题检查 PERMISSION capability 与平台签名 lineage。
  6. 增量安装问题分离 APK 签名、checksum 签名和 .idsig 验证。

11. 源码阅读路线 ​

  1. ApkSignatureVerifier.verify / verifySignaturesInternal:方案选择和降级边界。
  2. verifyV4Signature、verifyV3Signature、verifyV2Signature、verifyV1Signature:各方案 verifier。
  3. InstallPackageHelper 签名收集与更新兼容检查。
  4. SigningDetails.checkCapability:不同 capability 的安全语义。
  5. AppIdPermissionPolicy.shouldGrantPermissionBySignature:签名权限如何消费 PERMISSION capability。
  6. PackageInstallerSession 的 checksum/idsig 验证:增量安装的第二条完整性链。

APK 签名方案解决的是“文件来自谁、内容是否被改动”;SigningDetails capability 解决的是“这份签名能否继承数据、回滚或访问签名权限”。Android 17 将方案选择、更新兼容和权限授权分层处理,排查时不能用一个证书比较替代全部判断。