写在前面:一枚 .jks 文件能决定你 App 的生死
做 Android 出海的同学,多半听过或亲历过这类事故:
- 外包团队交付后联系不上,
.jks文件找不到了,应用没法更新; - 老同事离职,签名密钥存在他个人电脑里,没人知道密码;
- 密钥随手丢在 Git 仓库或共享网盘,被 Google 判定为高风险账号;
- 多个项目密钥混用,一个账号出问题连坐一片。
这些问题都不是技术难题,而是管理事故。Android 的签名机制决定了:同一个包名,只能用同一把密钥签名更新。密钥丢了,Google Play 上那个应用就废了,只能换包名重新上架,用户、评分、搜索权重全部归零。
这篇文章不讲复杂密码学,只讲出海团队日常怎么生成、保管、分发、备份 Keystore,以及怎么用 Google Play App Signing 把风险降到最低。
一、Android 签名机制到底在保护什么
1.1 为什么必须签名
Android 系统用签名做两件事:
- 身份验证:确认这个 APK/AAB 确实来自你,而不是被第三方篡改过的版本;
- 更新权限控制:只有用同一密钥签名的版本,才能覆盖安装旧版本。
也就是说,签名密钥是你在 Google Play 上某个应用的"身份证"。身份证丢了,你可以再办一张,但 Google Play 不认识新身份证和旧应用的关系,所以只能重新发布。
1.2 Keystore、Key alias、Certificate 三者的关系
很多新手把这几个概念搞混,先理清楚:
| 概念 | 是什么 | 类比 |
|---|---|---|
| Keystore(.jks / .keystore) | 存储密钥的容器文件 | 保险柜 |
| Key alias | 保险柜里某一把钥匙的名字 | 钥匙标签 |
| Keystore password | 打开保险柜的密码 | 保险柜密码 |
| Key password | 使用某把钥匙的密码 | 钥匙使用密码 |
| Certificate(.cer) | 公钥证书,可以公开 | 身份证正面 |
生成时一般两条命令:
# 生成新密钥
keytool -genkey -v -keystore release.keystore -alias myapp -keyalg RSA -keysize 2048 -validity 10000
# 查看证书信息
keytool -list -v -keystore release.keystore -alias myapp
注意:keyalg RSA 和 keysize 2048 是 Google Play 目前推荐的最小配置,不要再用过时的 1024。
二、生成 Keystore 的标准流程
2.1 本地生成 vs 服务器生成
推荐本地生成。在 CI/CD 服务器上生成密钥虽然方便,但增加了被日志、缓存、备份泄露的风险。本地生成后,只把必要信息注入 CI,密钥文件本身存在受控的密钥管理服务中。
2.2 生成时的 5 个注意事项
- 有效期设长一点:建议 25-30 年(
-validity 10000约 27 年)。Google Play 要求有效期至少到 2033 年以后。 - alias 命名规范:用项目名或包名,比如
myapp-release。不要用mykey、key0这种通用名。 - 密码分开管理:Keystore password 和 Key password 可以相同,但建议不同。尤其是多项目团队,统一用密码管理器生成 16 位以上随机密码。
- 组织信息真实填写:CN 填公司名或团队名,OU 填部门。这部分信息会进入证书,审核时偶尔会被查。
- 立即导出证书:生成后立刻导出
.cer或公钥指纹,方便后续在第三方 SDK、推送、支付平台配置时使用。
# 导出 SHA-1 / SHA-256 指纹,Firebase、推送 SDK 常用
keytool -list -v -keystore release.keystore -alias myapp | grep SHA
三、Google Play App Signing:最推荐的安全方案
3.1 传统签名的风险
传统模式下,你自己持有签名密钥(signing key),每次上传 AAB 前用本地密钥签名。问题是:只要密钥泄露一次,攻击者就能发布覆盖你应用的恶意版本。
3.2 Play App Signing 是什么
Google Play App Signing 让 Google 替你保管签名密钥。你只需要上传用上传密钥(upload key)签名的 AAB,Google 会用它保管的签名密钥重新签名后分发。
这样有两个好处:
- 签名密钥不离开 Google:大大降低泄露风险;
- 上传密钥可以重置:如果上传密钥泄露,可以在 Play Console 申请重置,不需要换包名。
3.3 如何启用 Play App Signing
新应用默认启用。老应用可以在 Play Console → 应用签名(App signing)页面选择"让 Google 管理并保护您的应用签名密钥"。迁移方式有两种:
- 导出并上传现有密钥(推荐):把现有
.jks导出为 PEPK 格式,上传到 Google; - 使用 Google 生成的密钥:Google 生成新签名密钥,你保留上传密钥。适合旧应用换包名或新应用。
迁移后,务必把原始签名密钥离线备份到多个安全位置。
3.4 上传密钥 vs 签名密钥的使用场景
| 场景 | 使用哪个密钥 |
|---|---|
| 日常构建 AAB 上传到 Play Console | 上传密钥 |
| 手动分发 APK 到测试群、第三方商店 | 签名密钥(如果已交给 Google 保管,则无法导出) |
| 其他渠道(华为、小米、官网) | 如果渠道要求与 Google Play 相同签名,需要用签名密钥;但 Play App Signing 下无法导出,需提前规划 |
如果你要走多渠道分发,启用 Play App Signing 前要想清楚:Google 保管签名密钥后,你无法导出私钥用于其他商店。这会影响国内安卓商店和部分 OEM 渠道。
四、团队里的密钥分发策略
4.1 绝对不要做的事
- ❌ 把
.jks文件发到微信、钉钉、飞书群里; - ❌ 把密钥密码写在代码注释、README、Confluence 里;
- ❌ 把密钥提交到 Git 仓库,包括私有仓库;
- ❌ 让外包团队或个人开发者单独持有唯一副本;
- ❌ 多项目共用同一个 Keystore,不同 alias 了事。
4.2 推荐的分级权限模型
| 角色 | 能访问什么 | 用途 |
|---|---|---|
| 技术负责人 | Keystore 文件 + 密码 | 灾难恢复、密钥重置 |
| CI/CD 系统 | 上传密钥 + 上传密码 | 自动构建 |
| 开发者 | 无 | 不需要接触密钥 |
| 外包/临时人员 | 测试密钥 | 仅测试包签名 |
4.3 密钥存储方案对比
| 方案 | 适合 | 风险 |
|---|---|---|
| 1Password / Bitwarden 附件 | 小团队 | 依赖密码管理器安全 |
| AWS Secrets Manager / GCP Secret Manager | 中大规模团队 | 需要 IAM 配置,有成本 |
| HSM / 硬件加密狗 | 金融、游戏大厂 | 成本高,管理复杂 |
| 离线加密 U 盘 + 纸质备份 | 所有团队 | 物理丢失风险,建议多份异地 |
我们一般给客户的建议是:日常 CI 用 Secret Manager,原始 Keystore 用离线加密 U 盘 + 纸质密码封存在保险柜。两手准备,任何一方出问题都不至于全军覆没。
五、备份与恢复:出事的时候能救命
5.1 备份什么
- 原始
.jks/.keystore文件 - Keystore password
- Key alias
- Key password
- 证书导出文件(
.cer或公钥指纹) - 生成时的组织信息记录
5.2 备份原则
- 3-2-1 原则:至少 3 份副本,2 种不同介质,1 份异地;
- 版本控制:每次密钥重置或迁移后,更新备份并标注日期;
- 定期演练:每半年做一次恢复演练,确认密码和文件都能正常打开;
- 分离存储:密钥文件和密码不要放在同一个地方。
5.3 常见恢复事故
- 备份了 Keystore 文件,但密码是 3 年前设置的、没人记得;
- 密码保存在离职员工的密码管理器里,账号没交接;
- 用旧版 Java 生成的 Keystore,新版 keytool 无法读取(格式兼容问题)。
建议备份时同时记录生成环境:JDK 版本、keytool 版本、命令参数。这些细节在多年后恢复时至关重要。
六、多项目团队的密钥管理规范
6.1 一项目一 Keystore
不要多个项目共用一个 Keystore。虽然 keytool 支持多个 alias,但一旦 Keystore 文件泄露,所有项目都受影响。每个项目独立 Keystore,可以把风险隔离。
6.2 命名规范示例
com-example-game-2026.keystore
com-example-social-2026.keystore
alias 也对应:
com-example-game
com-example-social
6.3 与 CI/CD 的集成
以 GitHub Actions 为例,不要把 Keystore 文件内容直接写进 workflow。用 base64 编码后存到 Repository secrets,构建时解码写入临时文件:
- name: Decode Keystore
run: echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 -d > release.keystore
- name: Build Release AAB
run: ./gradlew bundleRelease
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
构建完成后,立即删除临时文件,避免留在 runner 缓存里。
七、常见问题 FAQ
Q1:Keystore 丢了,还能找回吗?
A:理论上不能。Android 签名基于非对称加密,私钥只有你持有。如果没有启用 Play App Signing 且原始密钥丢失,只能换包名重新上架。如果已启用 Play App Signing,可以重置上传密钥,但签名密钥由 Google 保管,无法导出。
Q2:一个团队应该有几把上传密钥?
A:建议每个应用一把上传密钥。如果多个应用共用上传密钥,一旦泄露需要全部重置,影响面太大。大团队也可以按环境区分:生产、预发布、测试各一把。
Q3:外包团队让我把 Keystore 发给他们,该不该给?
A:不要直接发原始 Keystore。可以:
- 让对方提供签名后的 APK/AAB 给你,你重新签名;
- 或者启用 Play App Signing,只给上传密钥;
- 如果必须给,签完立刻在 Play Console 重置上传密钥。
Q4:签名密钥有效期快到了怎么办?
A:在密钥过期前,用新密钥重新签名并发布一次更新。注意:Android 系统要求新密钥的旧版本兼容性,建议提前 1-2 年开始规划轮换。Google Play 也支持 APK Signature Scheme v3 的密钥轮换,但实施前要充分测试。
Q5:测试包和正式包能用同一个 Keystore 吗?
A:技术上可以,但不推荐。测试包用 debug 或单独的测试 Keystore,正式包用 release Keystore。混用会增加正式密钥泄露的风险,也不方便权限管理。
写在最后:密钥管理是工程纪律,不是技术问题
见过太多团队把技术精力放在架构、性能、投放上,却在密钥管理这种"小事"上翻车。一次密钥丢失,可能比一次应用被拒的代价大得多——前者可能直接废掉一个应用,后者至少还能申诉。
如果你正在做 Google Play 上架,建议把 Google Play AAB 格式要求 和 Google Play 上架全流程 这两篇结合一起看。AAB 的签名逻辑和 Keystore 管理是绑在一起的,理解清楚能少走很多弯路。
如果你在密钥管理、多项目签名策略或者 Play App Signing 迁移上需要协助,可以点击页面右下角的咨询按钮联系巨游出海的技术顾问。我们帮出海团队做过大量密钥重建和账号迁移方案,能帮你把风险控制在最小范围。