如何使用 jarsigner 和 apksigner 对 APK 进行签名?两者有何区别?

解读

国内面试中,这道题常作为“APK 发布流程”环节的追问,用来区分候选人是否真正走过上线全链路。面试官期望听到三条主线:

  1. 命令行实操:密钥格式、签名算法、输出对齐。
  2. 版本差异:v1 scheme(JAR 签名)与 v2/v3 scheme(APK 签名)的验证时机、校验范围、性能影响。
  3. 国内合规:应用商店对 v2 签名的强制要求、工信部备案对签名指纹的回传。
    答不到“v2 签名必须开启”“apksigner 支持轮转”容易被打回“只是跑过 gradle 任务”。

知识点

  • 签名链:jarsigner 只生成 JAR 式 META-INF 内 .SF/.RSA,apksigner 额外生成整个 APK 的 v2/v3 签名分块。
  • 验证阶段:PackageManagerService 在安装时先按 v3→v2→v1 顺序尝试,任一通过即可;但国内主流市场(华为、OPPO、小米)已拒绝仅含 v1 的 APK。
  • 密钥格式:jarsigner 只认 JKS/PKCS12,apksigner 只认 PKCS8+PKCS12,且要求私钥算法为 RSA/ECDSA,DSA 已被废弃。
  • 对齐:jarsigner 签名后必须手动 zipalign;apksigner 要求“先对齐后签名”,否则直接报错。
  • 轮转:apksigner 支持 v3 scheme 的轮转签名,可实现旧签名到新签名的无缝切换,jarsigner 无此能力。
  • 性能:v2 签名把哈希树写进中央目录,安装时一次性校验,比 v1 逐文件解压哈希快 30% 以上,对低端机 OTA 升级尤其明显。
  • 安全:v2 签名覆盖整个 ZIP 结构,防止二次打包插入广告 SDK;v1 仅保护单个 entry,容易被“空文件注入”绕过。
  • 工具链:Android SDK 30+ 已将 apksigner 从 build-tools 独立,CI 镜像需显式安装;jarsigner 依赖 JDK,版本差异会导致签名算法兼容问题(JDK8 默认 SHA1,已被商店拒绝)。

答案

一、jarsigner 签名步骤(仅 v1,不推荐单独上线)

  1. 生成兼容密钥
    keytool -genkeypair -alias release -keyalg RSA -keysize 2048 -validity 9125 -keystore release.jks
  2. 对齐(必须在签名前,否则后续 zipalign 会破坏签名)
    zipalign -f -p 4 app-unsigned.apk app-aligned.apk
  3. 签名
    jarsigner -keystore release.jks -signedjar app-signed-v1.apk app-aligned.apk release
    参数说明:
    -sigalg SHA256withRSA -digestalg SHA-256 -tsa http://timestamp.digicert.com
  4. 验证
    jarsigner -verify -verbose -certs app-signed-v1.apk

二、apksigner 签名步骤(v1+v2+v3 同时开启,国内上线标配)

  1. 转换密钥到 PKCS12(若原为 JKS)
    keytool -importkeystore -srckeystore release.jks -destkeystore release.p12 -deststoretype PKCS12
  2. 对齐(必须先行)
    zipalign -f -p 4 app-unsigned.apk app-aligned.apk
  3. 签名
    apksigner sign --ks release.p12 --ks-key-alias release --out app-signed.apk app-aligned.apk
    可选参数:
    --v1-signing-enabled true --v2-signing-enabled true --v3-signing-enabled true
    --min-sdk-version 21 --max-sdk-version 34
  4. 验证
    apksigner verify -v --print-certs app-signed.apk
    输出中必须看到 “Verified using v1 scheme (JAR): true” “v2 scheme (APK Signature Scheme v2): true” 才算商店合规。

三、两者核心区别

  1. 签名位置:jarsigner 把签名写在 META-INF;apksigner 额外在 ZIP 中央目录与 EOCD 之间插入 v2/v3 分块。
  2. 校验范围:v1 只校验单个 entry,v2/v3 覆盖整个 APK,杜绝“二次打包”。
  3. 性能:v2 安装耗时降低 30%+,对国内低端机群显著。
  4. 轮转:apksigner 支持 v3 的轮转密钥,jarsigner 无。
  5. 对齐顺序:jarsigner 先签后对齐,apksigner 先对齐后签,顺序颠倒会直接失败。
  6. 商店政策:国内 Top200 渠道 2022 年起已拒绝仅 v1 签名,海外 Google Play 2023 年强制 v2。

拓展思考

  1. 混合签名场景:若企业旧 APK 使用 DSA+SHA1,需先在 v3 轮转中引入 RSA+SHA256,再逐步下线老密钥;轮转过程需保证旧用户覆盖率达到 90% 以上,否则系统会拒绝更新。
  2. CI 集成:在 GitLab-CI 中,应将 release.p12 放入受保护变量,使用 apksigner 的 --ks-pass env:KS_PASS 方式避免明文密码;同时利用 verify 步骤做二次校验,防止构建缓存污染。
  3. 合规审计:工信部备案要求上传签名 MD5/SHA256 指纹,apksigner verify 输出的证书指纹可直接用于后台比对;若使用 v3 轮转,需把新旧两条指纹同时备案。
  4. 安全左移:在 AGP 8+ 中,Google 默认关闭 v1 签名,若仍需兼容 Android 6 以下,需要手动开启 v1,但国内存量市场已低于 1%,可直接放弃。
  5. 性能极限:对车载与 Wear 这类低功耗设备,可关闭 v1 仅保留 v2,减少 1~2% 的存储占用;但需提前验证系统升级包是否支持,否则 OTA 会回退到 v1 校验导致安装失败。