接 Firebase、Google 登录、地图 SDK,或者给 Google Cloud API 密钥加 Android 限制时,都要填同一个东西:给 App 签名的那张证书的 SHA-1(很多时候还要 SHA-256)。Facebook 登录要的“密钥散列”(key hash)其实也是这个 SHA-1,只是换成了 Base64。常规做法是 keytool -list -v,得先装 JDK、开终端;Facebook 那个值还要再接一段 openssl:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
这个页面直接从文件里算出这些值,全程在浏览器里完成。
能读哪些文件
- Java 密钥库:JKS 和 JCEKS(
.jks、.keystore)。这两种格式里的证书是明文存的,所以不填密码也能看到每个别名、条目类型、创建时间和证书链。填了密码,会顺带校验密钥库末尾的完整性摘要。 - PKCS#12:
.p12、.pfx,以及 keytool 按 PKCS#12 写出的.keystore(JDK 9 起 keytool 默认就是这种格式)。这类文件通常把证书也加密了,需要密码。新算法(PBES2 + AES-256 + PBKDF2-HMAC-SHA256)和老 JDK、openssl pkcs12 -legacy写出的旧算法(PKCS#12 密钥派生 + RC2-40 或 3DES)都支持。 - APK 和 AAB:v1(JAR)、v2、v3、v3.1 签名方案里的签名者证书,用的是和 APK 分析器同一套解析代码。同一个签名者出现在多个方案里只显示一次,并标出所有方案;v3 密钥轮换后的新密钥会单独列为一个签名者。
- 证书文件:PEM(一张或多张)、DER、
.crt、.cer和.p7b。想看这些证书的全部字段和扩展,用 X.509 证书解析更合适。
结果里有什么
每个条目显示别名、PrivateKeyEntry 或 trustedCertEntry(沿用 keytool 的叫法),以及证书链里每张证书的主体、颁发者、有效期(是否已过期)、密钥算法和长度、签名算法、序列号。每张证书给出 SHA-1、SHA-256、MD5 三种指纹,各有三种写法,都带复制按钮:
| 写法 | 用在哪 |
|---|---|
冒号分隔的十六进制,5E:8F:16:… |
Firebase 控制台、Google Cloud API 密钥限制、Google 登录的 OAuth 客户端,也是 keytool 的输出格式 |
不带冒号的十六进制,5e8f16… |
只接受纯十六进制串的配置文件和接口 |
| 摘要的 Base64 | Facebook 登录的“密钥散列”(SHA-1 的 Base64) |
页面还会给出公钥 SHA-256 的 Base64 值,也就是 Android 网络安全配置 <pin digest="SHA-256"> 里要填的那个值。
该用哪张证书?用户实际安装的那个包是谁签的,就用谁。启用了 Play 应用签名之后,Google 会用自己保管的密钥重新签名,所以 Play 渠道的包要填 Play 管理中心“应用签名”页面上应用签名密钥的指纹;你本地的上传密钥库对应的是上传密钥。调试包用的是 ~/.android/debug.keystore(别名 androiddebugkey,密码 android),而且每台电脑各不相同。
我们怎么验证的
我们用 Temurin JDK 17.0.18 的 keytool 生成了 JKS、JCEKS、PKCS#12,以及用 -J-Dkeystore.pkcs12.legacy 写出的旧格式 PKCS#12;用 OpenSSL 4.0.3 生成了默认参数、-legacy、-descert 和不加密四种 PKCS#12;密钥覆盖 RSA 2048 和 EC P-256 / P-384,另外做了一条根证书、中间证书、叶子证书的链。单元测试把每个别名、条目类型、指纹、签名算法、密钥长度和序列号都和 keytool -list -v 的输出逐项比对,APK 签名者则和 Android build-tools 36.1.0 里 apksigner verify --print-certs 的输出比对。做这些测试文件时踩过一个坑:用 keytool -importkeystore 转成 PKCS#12 时,如果同时传了 -destkeypass,受信任证书条目会报 “trusted certificate entries are not password-protected”,然后直接不导入。转换之后发现少了某个别名,多半就是这个原因。
密钥库和密码怎么保证不外泄
- 文件在你设备上的 Web Worker 里读取,不会上传。
- 密码和文件一起交给这个 Worker,只用来校验完整性摘要或 MAC、解密证书部分,分析结束就丢弃。它不会保存在页面或浏览器存储里,不会写进任何日志,也不会出现在统计事件里。点“读取其他文件”会清空密码框。
- 私钥直接跳过,从不解密。这个页面没有办法导出或显示私钥。
- 统计事件只有粗粒度的值:成功或错误码、识别出的格式(
jks、pkcs12、apk等)、完整性校验结果、证书数量区间、文件大小区间和耗时区间。不含别名、名称或指纹。完整列表见隐私政策。 - 工具页不展示广告。
如何自己验证
- 打开浏览器开发者工具的 Network(网络) 面板,再拖入密钥库、输入密码。能看到的请求只有页面资源和 Worker 脚本
/_astro/cert-worker-*.js,没有一个带着你的文件或密码。 - 或者先打开页面,再断网(关掉 Wi-Fi,或在网络面板勾选 Offline),然后读取密钥库,照样能用。
- Worker 运行在
default-src 'none'; script-src 'self'; connect-src 'none'这条内容安全策略下,浏览器不允许它发出任何网络请求。 - 和你自己的工具对照:
keytool -list -v -keystore my.jks打印的 SHA-1、SHA-256,apksigner verify --print-certs app.apk打印的签名者摘要,应该与这里完全一致。
局限
- 不读 Bouncy Castle 的 BKS、UBER 密钥库,请先用
keytool -importkeystore -srcstoretype BKS -providerpath bcprov.jar …转换。 - JCEKS 的密钥(secret key)条目是 Java 序列化对象,遇到第一个就停止列举,后面的条目不会显示。
- 不支持公钥完整性模式的 PKCS#12,以及 MAC 用 PBMAC1 的文件(显示为“未校验”)。BER 编码(不定长、分段 OCTET STRING)的文件可以读取。
- APK 签名只列出、不验证。要验证请用
apksigner verify或 APK 分析器。不显示 Source Stamp 和 v4 签名(.idsig)。 - JKS 别名按 UTF-8 解码;Java 的 modified UTF-8 只在 NUL 字符和基本多文种平面以外的字符上有差别。
常见问题
获取 SHA-1 一定要密钥库密码吗?
JKS 和 JCEKS 不需要,证书没有加密。大多数 PKCS#12 文件需要,因为 keytool 和 OpenSSL 默认会加密证书部分。不填密码时,页面仍会列出条目和别名,并提示证书已加密。
这里的 SHA-1 和 Firebase 里填的不一样?
先确认看的是不是给已安装那个包签名的密钥:调试包对应调试密钥库,自己分发的包对应上传密钥,从 Google Play 安装的包对应 Play 管理中心里的应用签名密钥。
Facebook 的密钥散列和 SHA-1 是一回事吗?
是同样的 20 个字节。Facebook 要 Base64 写法(28 个字符,以 = 结尾),Firebase 要冒号分隔的十六进制。
相关教程
- Android 签名 SHA-1 / SHA-256 指纹怎么查:keytool、apksigner、signingReport 实测对比,Firebase、Google 登录和 assetlinks.json 该用哪把钥匙,以及 Facebook key hash 的坑。
- 用 REA 分析 Android APK:JADX、Apktool、ADB
- Android 开发者验证:找出包名和签名证书 SHA-256
- 安装包与现有包冲突怎么解决:多半是签名证书不一致