Android 证书从 0 到长期可维护:Keystore、签名与密钥轮换
从 keystore、alias 和证书指纹的关系开始,完成发布密钥创建、Gradle 配置、APK/AAB 验签、Play App Signing、CI 注入与灾难恢复。
Android 发布密钥不是一个普通构建文件。应用上线后,签名身份直接关系到后续版本能否覆盖安装。本文使用通用示例讲清创建、配置、验证和备份流程;真实密码、密钥文件和账号信息不得写入仓库或文章。
先分清四个概念
Android 签名配置中最常见的几个词容易混在一起:
- Keystore:保存一个或多个密钥条目的容器文件。
- Alias:Keystore 内某个密钥条目的名称。
- 私钥:用于给 APK 或 App Bundle 签名,必须严格保密。
- 证书:包含公钥和主体信息,可用于校验签名身份和计算指纹。
同一个 Keystore 可以包含多个 Alias,但正式项目通常让每个产品使用清晰、可追踪的发布密钥。不要因为技术上能共用,就让所有应用共用一个无法独立轮换的密钥。
先决定签名托管方式
自主管理应用签名密钥
团队自己保存最终 App Signing Key。优势是完全控制,风险是密钥丢失或泄露时恢复困难。使用不同应用商店或企业分发时,通常仍需要团队保管长期发布密钥。
Google Play App Signing
Google Play 托管最终应用签名密钥,团队日常上传使用 Upload Key:
- 本地或 CI 使用 Upload Key 给 AAB 签名。
- Google Play 验证上传身份。
- Play 使用 App Signing Key 为用户分发的 APK 签名。
启用后要分别记录 App Signing Key Certificate 和 Upload Key Certificate 的 SHA-1、SHA-256 指纹。第三方登录、地图、支付等平台可能要求填写其中一种,不能混用。
第一步:生成发布 Keystore
使用 JDK 自带的 keytool:
keytool -genkeypair -v \
-keystore product-release.jks \
-alias product-release \
-keyalg RSA \
-keysize 2048 \
-validity 10000
命令会交互式要求输入 Keystore 密码、密钥密码和证书主体。主体信息应使用团队认可的通用资料,不要填写临时昵称。
生成后立即检查:
keytool -list -v \
-keystore product-release.jks \
-alias product-release
保存以下信息:
- Keystore 文件名与受控存储位置。
- Store Password。
- Key Alias。
- Key Password。
- SHA-1 与 SHA-256 指纹。
- 创建日期、有效期和负责人。
密码与 Keystore 文件必须分开保存。不要把两者一起放在共享网盘目录中。
第二步:配置 Gradle Release 签名
签名参数不应直接写进 build.gradle。本地可以从不提交到 Git 的 keystore.properties 读取,CI 则优先使用加密环境变量或凭据文件。
Groovy DSL 示例:
android {
signingConfigs {
release {
storeFile file(System.getenv("ANDROID_KEYSTORE_PATH"))
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias System.getenv("ANDROID_KEY_ALIAS")
keyPassword System.getenv("ANDROID_KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
}
}
}
Kotlin DSL 示例:
android {
signingConfigs {
create("release") {
storeFile = file(System.getenv("ANDROID_KEYSTORE_PATH"))
storePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias = System.getenv("ANDROID_KEY_ALIAS")
keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
}
}
}
配置完成后,不要只看 Gradle 的 BUILD SUCCESSFUL。必须对最终产物验签。
第三步:构建 APK 与 AAB
常用发布命令:
./gradlew clean assembleRelease
./gradlew bundleRelease
APK 适合直接安装或部分渠道分发,AAB 通常用于 Google Play。不同渠道对包格式和签名方案要求可能不同,发布前应核对目标商店当前规则。
如果项目存在 productFlavor,要确认构建变体使用的是正式 applicationId、版本号、服务配置和签名配置,避免把测试环境包提交到线上应用。
第四步:验证 APK 签名
使用 Android SDK Build Tools 中的 apksigner:
apksigner verify --verbose --print-certs app-release.apk
输出应至少检查:
- 验签结果为成功。
- Signer 数量符合预期。
- SHA-256 指纹与登记记录一致。
- v1、v2、v3 或 v4 签名方案符合目标系统与渠道要求。
查看 APK 的包名和版本:
aapt dump badging app-release.apk | head
签名正确但包名、versionCode 或渠道配置错误,同样不能作为可发布产物。
第五步:验证 AAB 与签名身份
AAB 本身是上传产物,最终安装 APK 通常由应用商店生成。可以先查看 JAR 签名信息:
jarsigner -verify -verbose -certs app-release.aab
使用 Google Play App Signing 时,还应在 Play Console 的应用完整性页面记录:
- App Signing Key Certificate 指纹。
- Upload Key Certificate 指纹。
- 当前密钥升级或轮换状态。
对接 Firebase、Google Sign-In、地图或其他开放平台时,先确认对方要求的是开发证书、Upload Key 还是最终 App Signing Key,避免指纹填对了格式却填错了证书。
CI 中安全注入 Keystore
CI 的基本原则是:密钥只在任务运行期间出现,日志中不可见,任务结束后删除。
一种做法是将 Keystore 作为 CI 的加密文件保存,运行时写入临时目录:
export ANDROID_KEYSTORE_PATH="$RUNNER_TEMP/product-release.jks"
test -f "$ANDROID_KEYSTORE_PATH"
keytool -list \
-keystore "$ANDROID_KEYSTORE_PATH" \
-alias "$ANDROID_KEY_ALIAS" \
-storepass "$ANDROID_KEYSTORE_PASSWORD" > /dev/null
./gradlew bundleRelease
构建日志不要启用会回显环境变量的调试模式。任务结束后清理临时 Keystore、生成的属性文件和解码中间文件。
密钥备份与灾难恢复
至少准备两份加密备份,并放在不同故障域:
- 团队密码库或受控密钥管理系统。
- 离线加密介质或独立的安全备份位置。
备份恢复演练比“已经备份”更重要。应定期在隔离环境验证:
- Keystore 文件可以读取。
- 密码和 Alias 正确。
- 可以签出测试 APK。
- 签名指纹与线上版本一致。
如果只有某位成员的电脑能构建 Release,这不是流程,而是单点故障。
Upload Key 丢失怎么办
启用 Google Play App Signing 后,如果丢失的是 Upload Key,可以按 Play Console 当前流程申请重置 Upload Key。重新生成上传密钥后,提交新的证书供平台审核。
如果未启用托管签名,并且最终应用签名私钥彻底丢失,后果会严重得多:旧用户通常无法直接安装由新密钥签名的更新。处理能力取决于目标商店、Android 版本和是否提前启用了可用的密钥升级机制。
因此,是否启用 Play App Signing 和如何备份自管密钥,应在首次正式发布前决定,而不是丢失后再补救。
常见错误怎么排查
Keystore was tampered with, or password was incorrect
可能是 Store Password 错误,也可能拿错 Keystore。先核对文件哈希和受控记录,不要连续试错导致凭据暴露在日志中。
Alias 不存在
运行 keytool -list -keystore product-release.jks 查看实际 Alias。文件名正确不代表内部条目名称正确。
签名后无法覆盖安装
对比新旧 APK 的证书指纹、applicationId 和版本号。覆盖安装要求包名一致、签名身份兼容,并且 versionCode 满足升级条件。
第三方登录提示签名不匹配
分别检查 Debug、Release、Upload Key 和 App Signing Key 的 SHA-1/SHA-256。线上商店安装包通常不是 Debug 证书,也不一定是 Upload Key。
CI 本地成功、线上失败
检查 CI 是否拿到了正确 Keystore、Alias 和 Release 变体;再使用 apksigner --print-certs 对 CI 产物做最终验证,而不是只比对配置文件。
密钥轮换策略
密钥轮换必须先确认分发平台和最低 Android 版本的支持范围。安全的基本流程是:
- 记录当前线上签名指纹和分发平台状态。
- 按平台能力申请密钥升级或 Upload Key 重置。
- 在测试轨道验证升级安装、登录、推送和第三方 SDK。
- 更新所有依赖证书指纹的开放平台配置。
- 保留旧密钥的受控归档,不立即销毁。
不要把“重新生成一个 Keystore”当作轮换。新密钥能否被现有安装接受,取决于完整的签名升级链路。
最终检查清单
- Keystore、Alias 和密码已记录并分开保管。
- SHA-1、SHA-256 指纹已登记。
- Release 配置没有硬编码密码。
- APK 已通过
apksigner verify。 - AAB 已通过
jarsigner基础校验。 - 包名、versionCode、versionName 和环境配置正确。
- 已区分 Upload Key 与 App Signing Key。
- CI 日志不会输出密钥或密码。
- 已完成至少一次备份恢复演练。
Android 证书管理的底线是:任何一次正式构建都能确认“使用了哪把密钥”,任何密钥事故都能明确“如何恢复或轮换”。做到这两点,签名才真正进入可维护状态。