F-Droid 签名验证与 APK 哈希检查:三层信任链详解
信任链的三层结构
F-Droid 为每个仓库建立了三层信任关系,从上到下逐层验证:
第一层:仓库索引签名
↓
第二层:APK 数字签名
↓
第三层:APK 哈希值
只有三层全部验证通过,F-Droid 才会把 APK 标记为可信并允许安装。
第一层:仓库索引的 GPG 签名
每个 F-Droid 仓库都有一个索引文件(index-v1.jar),里面包含了该仓库所有应用的元数据:版本号、APK URL、APK 签名、哈希值、权限列表等。
这个索引文件本身用 GPG 签名。F-Droid 客户端内置了官方仓库的公钥,同步仓库时:
- 下载索引文件和签名
- 用内置公钥验证 GPG 签名
- 签名验证通过 → 索引内容可信
- 签名验证失败 → 拒绝同步,显示警告
这样做的意义是:即使仓库服务器被攻击,攻击者也无法替换索引内容——因为没有私钥就造不出合法签名。
官方仓库的签名密钥
F-Droid 官方仓库的签名密钥指纹:
GPG 密钥名:F-Droid <admin@f-droid.org>
主密钥指纹:37D2 C987 89D8 3119 C850 D7BE A957 9C06 4A3B E5E9
F-Droid 客户端在编译时硬编码了这个指纹,只信任匹配它的签名。
第二层:APK 的数字签名
Android 要求所有 APK 必须用开发者证书签名。F-Droid 构建流程在编译完 APK 后,会用 F-Droid 自己的密钥签名每个 APK。
APK 签名包含:
- 应用的 SHA-256 签名哈希
- 签名证书的完整信息(开发者、密钥指纹、有效期等)
F-Droid 在从索引中解析 APK 时:
- 从索引中读取该 APK 应该有的签名哈希
- 下载 APK 后计算实际签名哈希
- 比对两个哈希 → 一致则通过
这一层确保了 APK 文件本身没有被篡改(无论是下载过程中被中间人替换,还是仓库服务器上 APK 被替换)。
第三层:APK 的 SHA-256 哈希
最后一层是最简单也最直观的保护:每个 APK 文件的 SHA-256 哈希值。
- 索引文件中记录了每个 APK 的 SHA-256
- 下载完成后,F-Droid 计算 APK 的实际 SHA-256
- 比对一致 → 文件下载过程没有被篡改
这一层和第二层有点重叠——APK 签名其实也隐含了文件完整性。但哈希检查是独立的补充,万一签名检查有遗漏,哈希检查还能兜住。
验证失败时会怎样
F-Droid 遇到任何一层验证失败:
- 不安装该 APK
- 在界面上显示警告信息
- 在日志中记录详细原因
- 如果是仓库索引签名失败,整个仓库的应用都不可用
F-Droid 不会静默接受验证失败的 APK——要么验证通过,要么不装。
用户手动验证的方法
高级用户可以手动验证 APK 的完整性:
# 方法一:用 Android SDK 的 apksigner
apksigner verify --verbose F-Droid.apk
# 方法二:对比 SHA-256 哈希
sha256sum F-Droid.apk # 对比官方页面的值
# 方法三:用 GPG 验证签名
gpg --verify F-Droid.apk.asc F-Droid.apk
普通用户可以跳过——F-Droid 客户端在后台自动完成了所有验证。
关于第三方仓库的签名
当你添加第三方仓库(如 IzzyOnDroid)时:
- 该仓库有自己的签名密钥
- F-Droid 会提示你确认信任这个新密钥
- 确认后,F-Droid 才会信任该仓库的索引和 APK
这是一个重要的安全决策——你需要确保自己知道添加的是什么仓库,不要随便信任陌生密钥。