Android签名轮转
Android签名轮转(Signature Rotation)是Android应用签名机制中的一项功能,允许开发者在不影响应用更新和用户数据的情况下,逐步更换应用的签名密钥。这种机制主要用于增强应用安全性,例如在密钥泄露、密钥过期或需要升级签名算法时,能够平滑过渡到新的签名密钥。
Android签名轮转的原理
1. 签名轮转的概念
- APK签名方案:Android支持多种签名方案(V1、V2、V3、V4),签名轮转主要依赖于APK签名方案V3(API 28引入,Android 9.0)及其扩展的轮转功能。
- 签名轮转机制:
- 多签名支持:V3签名方案允许APK同时包含多个签名密钥(旧密钥和新密钥),Android系统会根据设备支持的签名选择合适的密钥进行验证。
- 签名历史记录:V3签名方案在APK中存储了一个签名历史(Lineage.bin),记录了从旧密钥到新密钥的过渡关系,确保系统能够信任新密钥。
- 向下兼容:即使设备不支持新签名,旧签名仍然有效,保证旧设备上的应用更新正常进行。
2. 签名轮转的工作流程
- 初始状态:
- 应用使用单一密钥(旧密钥)签名,安装在用户设备上。
- 引入新密钥:
- 开发者生成新密钥,并使用旧密钥和新密钥同时签名新版本APK。
- APK中包含一个签名历史,证明新密钥是由旧密钥授权的。
- 过渡阶段:
- 设备验证APK时,使用旧密钥(如果支持)或新密钥(如果支持V3签名)。
- 新版本安装后,设备记录新密钥,准备接受未来仅用新密钥签名的APK。
- 完全轮转:
- 在确保大部分用户设备已更新到支持新密钥的版本后,开发者可以选择仅用新密钥签名新版本APK。
- 旧密钥逐渐废弃,但签名历史仍保留以支持旧设备。
3. 技术细节
- V3签名方案的结构:
- V3签名块(APK Signing Block)包含签名数据和签名历史(Lineage.bin)。
- 签名历史是一个链式结构,每个节点记录一个密钥及其与前一个密钥的授权关系。
- 签名验证逻辑:
- 系统首先检查APK是否包含有效的V3签名。
- 如果设备支持V3,则验证签名历史,确认新密钥是否由旧密钥授权。
- 如果设备不支持V3,则回退到V2或V1签名验证(需要旧密钥仍然存在,对于蓝信9.2以下的版本,仅支持v1、v2签名)。
- 兼容性:
- Android 9(API 28)及以上支持V3签名。
- 旧设备依赖V1/V2签名,因此轮转期间需要保留旧密钥的签名。
实现Android签名轮转的步骤
1. 准备工作
- 工具:
- keytool:用于生成新密钥。
- apksigner:Android SDK提供的签名工具,支持V3签名。
生成新密钥:
keytool -genkeypair -v -keystore new-keystore.jks -alias new-alias -keyalg RSA -keysize 2048 -validity 10000
2. 创建签名历史(Lineage)
- 使用apksigner的rotate命令生成签名历史文件,记录旧密钥和新密钥的关系:
bash
apksigner rotate --old-signer --ks old-keystore.jks --ks-alias old-alias --new-signer --ks new-keystore.jks --ks-alias new-alias --out lineage.bin
- --old-signer:指定旧密钥。
- --new-signer:指定新密钥。
- --out:输出签名历史文件(lineage.bin)。
3. 使用旧密钥和新密钥签名APK
使用apksigner对APK进行多重签名,并嵌入签名历史(兼容Android 9以下版本):
apksigner sign --ks old-keystore.jks --ks-alias old-alias --next-signer --ks new-keystore.jks --ks-alias new-alias --lineage lineage.bin --out app-signed.apk app-unsigned.apk
- --next-signer:指定新密钥。
- --lineage:指定签名历史文件。
- 验证签名:
apksigner verify -v app-signed.apk
- 确保APK包含V1、V2和V3签名,且签名历史正确。
4. 发布过渡版本(包含新旧密钥签名,目的兼容Android 9以下版本)
- 将使用旧密钥和新密钥签名的APK发布到各个分发渠道。
- 用户更新到此版本后,设备会记录新密钥,准备接受未来仅用新密钥签名的APK。
5. 完全过渡到新密钥
- 在确保大部分用户已更新到过渡版本后,可以仅用新密钥签名新版本APK:
apksigner sign --ks new-keystore.jks --ks-alias new-alias --lineage lineage.bin --out app-signed.apk app-unsigned.apk
- 注意:仍需保留签名历史(lineage.bin),以便系统验证新密钥的合法性。
lineage.bin 介绍
lineage.bin 文件是 Android 签名轮转(Signature Rotation)机制中用于存储签名历史文件。它记录了 APK 签名密钥的演变关系,确保新密钥能够被系统信任,同时维护旧密钥与新密钥之间的授权链。
一、lineage.bin 的作用
- 核心功能:
- lineage.bin 是 APK 签名方案 V3(及以上)中用于支持签名轮转的二进制文件。
- 它记录了签名密钥从初始密钥到当前密钥的授权链。
- 系统通过解析 lineage.bin,验证新密钥是否由旧密钥授权,从而允许使用新密钥签名的 APK 覆盖安装旧密钥签名的 APK。
- 使用场景:
- 在签名轮转时,开发者需要将旧密钥和新密钥的签名历史写入 lineage.bin,并嵌入到 APK 的 V3 签名块中。
- Android 9(API 28)及以上设备会读取 lineage.bin 来支持签名轮转。
二、lineage.bin 中存储的内容
lineage.bin 是一个结构化的二进制文件,包含以下核心信息:
1. 签名密钥的链式关系(Lineage)
- 结构:
- 签名历史是一个有序的密钥列表,形成一个链式结构(从最早的密钥到最新的密钥)。
- 每个节点代表一个签名密钥,包含该密钥的公钥信息及其与前一个密钥的授权关系。
- 内容:
- 公钥:每个密钥的公钥(通常是 X.509 证书中的公钥部分),用于验证签名。
- 签名算法:每个密钥使用的签名算法(如 RSA-SHA256、ECDSA-SHA256)。
- 授权签名:当前密钥的公钥由前一个密钥的私钥签名,证明其合法性。这种签名形成信任链,确保新密钥得到旧密钥的授权。
2. 版本和格式信息
- 文件格式版本:
- lineage.bin 的格式可能随 Android 版本或 apksigner 工具更新而变化,文件头通常包含版本信息以确保兼容性。
- 序列化格式:
- 数据以二进制格式序列化,遵循 Android 签名方案的内部协议(基于 ASN.1 或类似结构)。
三、lineage.bin 的生成和使用
生成
使用 Android SDK 的 apksigner 工具生成 lineage.bin,例如:
apksigner rotate --old-signer --ks old-keystore.jks --ks-alias old-alias --new-signer --ks new-keystore.jks --ks-alias new-alias --out lineage.bin
- --old-signer:指定旧密钥。
- --new-signer:指定新密钥。
- --out:输出签名历史文件 lineage.bin。
- 生成时,apksigner 会:
- 提取旧密钥和新密钥的公钥。
- 使用旧密钥的私钥对新密钥的公钥进行签名,形成授权关系。
- 将这些信息序列化为二进制格式,保存到 lineage.bin。
嵌入 APK
在签名 APK 时,lineage.bin 被嵌入到 V3 签名块(APK Signing Block)中:
apksigner sign --ks old-keystore.jks --ks-alias old-alias --next-signer --ks new-keystore.jks --ks-alias new-alias --lineage lineage.bin --out app-signed.apk app-unsigned.apk
- V3 签名块包含:
- 旧密钥和新密钥的签名数据。
- 签名历史(从 lineage.bin 读取)。
问题解答
- 使用轮转签名的apk否可以被旧签名的APK覆盖安装?
- Android 9以下版本:可以
- Android 9以及以上版本:不可以
- 使用二次轮转的签名,签名的apk是否可以覆盖安装最原始的签名签的apk(9.0+版本)
- 在 Android 9.0+ 上,使用二次轮转签名的 APK(使用 key3 签名,包含 key1 -> key2 -> key3 的签名历史)可以覆盖安装使用原始签名(key1)的 APK。
- 可以多次轮转吗?
可以。签名历史支持多次轮转,每次添加新密钥到历史链中。
- 签名轮转是否有次数限制?
- 没有明确次数限制,V3 签名方案支持多次轮转,通过 lineage.bin 记录信任链。