借腾讯开源 VasDolly,谈谈 Android 签名和多渠道打包的原理!
一、前言
Hi,大家好,我是承香墨影!
当我们需要发布一款 App 到应用市场的时候,一般需要我们针对不同的市场生产不同的渠道包,它们使用的是同一套代码,只是会包含一些各自的渠道信息,用于我们做数据分析。
前几天,企鹅电竞团队开源了自己的 Android Apk 多渠道打包工具:VasDolly,比美团的 Walle 更全面一些。
正好借这个机会,来讲解一下 Android 的不同版本的签名机制的差异。
二、Android 的签名
2.1 应用签名
通过对 Apk 进行签名,开发者可以证明对 Apk 的所有权和控制权,可用于安装和更新其应用。而在 Android 设备上的安装 Apk ,如果是一个没有被签名的 Apk,则会被拒绝安装。
在安装 Apk 的时候,软件包管理器也会验证 Apk 是否已经被正确签名,并且通过签名证书和数据摘要验证是否合法没有被篡改。只有确认安全无篡改的情况下,才允许安装在设备上。
简单来说,APK 的签名主要作用有两个:
- 证明 APK 的所有者。
- 允许 Android 市场和设备校验 APK 的正确性。
而在 Android 中,支持两种应用签名的方案:
- 基于 JAR 签名方案(v1 方案)。
- Android Nougat(7.0) 中引入的 APK 签名方案 v2(v2 方案)。
既然签名方案在升级,v1 方案一定有一些缺陷的地方,接下来我们先来了解一下这两个方案的细节。
2.2 v1 签名方案
v1 签名方案,并不会保护 Apk 内的所有内容,有一些例外部分,被修改也并不会导致签名失效。例如:ZIP 元数据。
这样,在验证 APK 签名的时候,就需要处理大量不可信(尚未经过验证)的数据结构,然后还需要过滤并舍弃掉这部分不受签名保护的数据,再进行签名校验。也就是说你可以在已签名的文件中,增加一些不被签名保护的内容,这将导致受攻击的可能增大。
另外,v1 方案是对 APK 内部的被保护的原始文件(未压缩),单独进行计算数据摘要,所以在验证期间,也需要对每个文件进行减压再进行签名校验,来验证是否被篡改。所以在验证 APK 签名的时候,必须解压 APK 的所有已压缩的文件条目进行数据摘要的校验,而这些,都将需要花费更多的时间和内存。
正是因为 v1 方案的缺陷,Android 7.0 开始,才引入了 APK 签名方案 v2。
2.3 v2 签名方案
APK 签名方案 v2 是一种全文件的签名方案,该方案能够对 APK 所有受保护的部分进行签名保护,从能能够发现它们被篡改。
在 APK 验证期间,v2 方案会将 APK 文件视为 Blob,并对整个文件进行签名检查。对 APK 进行的任何修改(包括对 ZIP 元数据的修改),都会使 APK 签名作废。
使用 APK 签名方案 v2 进行签名时,会在 APK 文件中,插入一个 APK 签名块,该分块位于“ZIP 中央目录”部分之前并紧邻该部分。在“APK 签名分块”内,v2 签名和签名者身份信息会存储在 APK 签名方案 v2 分块中。
该分块包含多个 "ID-值" 对,所采用的封装方式有助于更轻松地在 APK 中找到该分块。APK 的 v2 签名会存储为一个 "ID-值" 对,其中 ID 为 0x7109871a。
上图是签名前后,APK 文件结构的对比。可以看到在 v2 已签名的 APK 中,包含了 4 个部分。
- ZIP 条目的内容。
- APK 签名分块(APK Signing Block)。
- ZIP 中央目录。
- ZIP 中央目录结尾。
APK 签名方案 v2 负责保护第 1、3、4 部分的完整性,以及第 2 部分包含的 APK 签名方案 v2 分块中的 signed data 分块的完整性。
第 1、3 和 4 部分的完整性通过其内容的一个或多个摘要来保护,这些摘要存储在 signed data 分块中,而这些分块则通过一个或多个签名来保护。
第 1、3 和 4 部分的摘要信息,采用一种类似两级 Merkle 树的方式进行计算,所以效率上会提高很多。这个就比较复杂了,大家有兴趣再深入研究。
既然已经有了 v2 方案,我们是否可以完全舍弃 v1 方案,只使用 v2 方案呢?
从官方文档了解到,因为 APK 签名方案 v2 是在 Android 7.0 中引入的,为了使 APK 可以在 Android 6.0 以及更低的版本中正常安装,应该先使用 JAR签名(v1 签名)对 APK 进行签名然后再使用 v2 方案对其进行签名。
而在实际测试中,这种仅使用 v2 方案不使用 v1 方案的情况,是可以编译通过的,并且在 Android 7.0 之上的设备上也是可以安装和运行,但是在 7.0 之下,因为不检测 v2 签名,而 APK 又不存在 v1 签名,在安装的时候,会提示没有签名的错误。
为了保证兼容性的问题,在 7.0 以上的设备中,也是兼容了 v1 签名的方案。所以 v2 签名方案,暂时并不是一个强制的方案。
优先级是:优先校验 v2 方案,没有或者不存在校验机制,则校验 v1 方案。
2.4 两种方案对比
到这里,我想应该对 v1 和 v2 签名方案应该已经有一定了解了。v2 签名既然是升级版,就是为了解决 v1 签名方案的一些问题。
1、效率问题
v1 方案,会在 /META-INF/MANIFEST.MF 文件中,记录所有需要校验签名的文件的数据摘要,并且这里生产数据摘要是依据压缩前的源文件,而在打包的过程中,这些文件又会被压缩。
所以在验证签名的过程中,需要先解压出原始文件,才能计算数据摘要从而验证它,而这个过程,会消耗更多的时间和内存。
v2 签名方案,是对 APK 文件本身进行数据摘要计算,也就是说它只计算一个文件(v1 会计算 Apk 内的很多文件),这样就不存在解压 APK 的操作,效率和内存都得到优化。
2、安全问题
v1 方案,它只会对 APK 内部,部分文件进行校验,并不会对 APK 的完整性进行校验。因此,签名后,我们依然可以修改 APK 文件,例如:美团的多渠道方案就是在 /META-INF/ 目录下,添加一个空文件,使用文件名来标志渠道。
而 v2 签名是针对整个 APK 进行校验,所以对 APK 的任何改动,都无法通过 v2 签名的验证,这样安全性会更高。
关于签名校验的耗时上,主要影响的是安装耗时,这里有一份实验室数据(Nexus 6P、Android 7.1.1)可供参考。
差不多 2.x 倍的差距,v2 签名对 APK 的安装还是有不少提升的。
三、常见的多渠道方案
多渠道的需求点,在于同一个 App,需要有一些不同来区分它们,最常见的场景就是 Android 国内的市场,对不同的市场使用不同的渠道包,它们的代码是一致的,只是一个渠道号的数据不一样。这样区分的好处在于数据更清晰,我们知道那个渠道的用户是优质用户。
3.1 被废弃的方案
既然签名方案在升级,多渠道的方案也需要升级。曾经有一写可行的方案,基本上已经被废弃了。例如:Gradle Plugin 配置不同的 Flavors、利用 ApkTool 解包改数据再重新打包并重新签名。
这些方案都有一些局限性。例如 Gradle Flavors 方案,每个渠道都需要重新打包,非常的耗时,并且生成的渠道包 DEX 的 CRC 值都不一致,不利于我们使用热更新的方案;而 ApkTool 的方案,首先 解包→打包→重新签名 的过程同样耗时,其次在于,不稳定,可能升级了 Gradle Plugin 的版本之后,会导致解包失败。
所以我们在想,一个高效的多渠道打包方案,有几个关键单需要注意。
- 不能破坏签名。
- 不能重新打包。
- 读取信息,必须高效。
不破坏签名就限制了不能解包以及重新签名,势必对效率有所提高。
而除了 VasDolly 之外,市面上流传比较广的就是 美团 给出的 Walle 方案,这里就以它们的原理进行简单的讲解。
3.2 v1 签名下的常见多渠道方案
v1 下对签名的校验比较弱了,美团给出了完整的解决方案。前面也介绍过,就是在以及打包签名好的 Apk 中,向 /META-INF 目录下,写入一个空文件,以文件名来标识渠道号。
首先,使用这样的方案,并不会破坏 v1 签名,所以效率会很高。
而在腾讯的 VasDolly 中,其实是向 Apk 文件的 EOCD 部分,增加渠道的信息。
APK 文件本质上是一个 ZIP 包,而 EOCD 就是上图所示的第三部分,并且这部分是不被 v1 签名校验的,可以用来记录我们的渠道信息。
3.3 v2 签名下的多渠道方案
v2 方案下,其实腾讯 VasDolly 和美团的 Walle 方案,并没有什么区别,因为验证更强了,大家可操作的区域就只有那么多了。
前面也提到过,使用 v2 签名后的 APK 文件结构中,插入了一个 APK Signing Block 的签名块。
在这个 APK 文件结构下,只有 块2 ,也就是记录 APK v2 签名信息的区域,不是全部参与 v2 签名的校验,所以大家都在这部分做文章。
APK Signing Block 中包含了多个 "ID-值" 的键值对,而 v2 签名的信息,会记录在 ID 为 0x7109871a 中,而不会校验其他 ID 下的值。
因此,基于 v2 签名的多渠道方案就这样诞生了:在 APK 签名块中添加一个额外的 ID-值,用于记录渠道信息。
四、可商用的多渠道打包方案
在 VasDolly 开源之前,市面上支持 v2 签名的多渠道打包方案,就属美团的 Walle 了,下面简单比对一下它们的优缺点。
具体你想使用哪种方案,就看你的实际需要了。
美团 Walle Github 地址:
https://github.com/Meituan-Dianping/walle
Walle 扫码直达
腾讯 VasDolly Github 地址:
https://github.com/Tencent/VasDolly/
VasDolly 扫码直达
部分图片来源以及参考资料:
https://source.android.com/security/apksigning/v2
https://github.com/Tencent/VasDolly/wiki/VasDolly%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86
https://tech.meituan.com/mt-apk-packaging.html
今天在公众号后台回复成长『成长』,将会得到我整理的一些学习资料,也能回复『加群』,一起学习进步。
推荐阅读:
- 站在Android开发的角度,聊聊Airbnb的Lottie
- 漫画:Git 二分 Debug,火速定位出错代码!
- 找了一天找不到 Bug ? 试试 Git 的二分法吧!!!
- 如何更精准的在 Github 上搜索开源库?你需要这些技巧!
- Android 开发,遇上 Emoji 头疼吗?
借腾讯开源 VasDolly,谈谈 Android 签名和多渠道打包的原理!的更多相关文章
- Android Studio下多渠道打包
Android Studio下实现多渠道打包 直接上步骤 步骤 1. 清单文件添加属性(以友盟统计为例) 在application标签下添加meta-data属性 <application -- ...
- android 7.0 多渠道打包 - 美团开源工具Walle 命令行打包
在Android 7.0(Nougat)推出了新的应用签名方案APK Signature Scheme v2后,之前快速生成渠道包的方式(美团Android自动化之旅-生成渠道包)已经行不通了,对此美 ...
- android 签名、混淆打包
1.android 签名 使用eclipse导出带签名的apk,最简单的方式. 最后一步finish ,就能导出一个xxx.keystore的文件了. 下次再发布新版本的时候,使用这个生成的签名继续使 ...
- 【Android Studio探索之路系列】之十:Gradle项目构建系统(四):Android Studio项目多渠道打包
作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.co ...
- Android几种多渠道打包
1.什么是多渠道打包 在不同的应用市场可能有不同的统计需求,需要为每个应用市场发布一个安装包,这里就引出了Android的多渠道打包.在安装包中添加不同的标识,以此区分各个渠道,方便统计app在市场的 ...
- Android 使用 Gradle 多渠道打包
安卓开发完毕.对于一个开放应用而言,我们须要公布到不同的应用市场,同一时候我们也须要统计不同市场的用户下载量. (通过启动应用后获取不同市场apk中的不同值来区分) 以下用一个详细的实例来说明: 1. ...
- Android Studio + gradle多渠道打包
通过工具栏的Build->Build Apk 好像只能打包第一个Module(eclipse里面是Project的概念),怎么多渠道打包呢?目前好像只能一个一个的打 首先在清单文件里设置个变量: ...
- 【腾讯Bugly干货分享】Android 新一代多渠道打包神器
关于作者: 李涛,腾讯Android工程师,14年加入腾讯SNG增值产品部,期间主要负责手Q动漫.企鹅电竞等项目的功能开发和技术优化.业务时间喜欢折腾新技术,写一些技术文章,个人技术博客:www.lt ...
- Android 新一代多渠道打包神器
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:李涛 ApkChannelPackage是一种快速多渠道打包工具,同时支持基于V1签名和V2签名进行多渠 ...
随机推荐
- 手把手教学系列:从零开始配置VPS服务器
1.什么是VPS? 百度百科:VPS(Virtual Private Server 虚拟专用服务器)技术,将一台服务器分割成多个虚拟专享服务器的优质服务. 通俗地讲,可以认为就是一台放在机房机架上的服 ...
- lambda高级进阶--表达式参数
1,现在我们封装一个方法,来提供一个比较器,显然比较器是拥有两个参数的--用来比较的两个值. public class Linkin { public static String[] sort(Str ...
- myeclipse取消js校验
最近玩一个新的项目,项目里面集成了别的项目,在从SVN上第一次荡下来的时候编译的时候老是校验jq文件,老是被卡主,设置myeclipse环境的时候我已经取消了所有的js校验了,但是还是不行.恼火之余, ...
- 【原创】python实现视频内的face swap(换脸)
1.准备工作,按博主的环境为准 Python 3.5 Opencv 3 Tensorflow 1.3.1 Keras 2 cudnn和CUDA,如果你的GPU足够厉害并且支持的话,可以选择安装 那就先 ...
- Java NIO 之 Selector
Selector是SelectableChannel的多路选择器,配合SelectableChannel实现非阻塞IO. 详见代码 /** * Selector 是 SelectableChannel ...
- execl列数据成等差递增递减
如上图若想以10,20,30...这样递增: 1).首先需选中10,20所在的单元格,鼠标移至20所在的单元格右下角 2).此时会出现一个十字"十"符号,点击直向下拖动至某个地方, ...
- XAML: 自定义控件中事件处理的最佳实践
在开发 XAML(WPF/UWP) 应用程序中,有时候,我们需要创建自定义控件 (Custom Control) 来满足实际需求.而在自定义控件中,我们一般会用到一些原生的控件(如 Button.Te ...
- 管理Mac的Python环境
问题描述 我的Mac自带了版本为2.7.10的Python却没有用于管理依赖的pip工具.而我在使用刚开始学习Python时,从其官网下载了安装脚本安装了3.6版本的Python.脚本自动配置了环境变 ...
- 洛谷 [p2294] [HNOI2005] 狡猾的商人
差分约束做法 又是一道转换成前缀和的差分约束题,已知从s月到t月的收入w,设数组pre[i]代表从开始到第i个月的总收入 构造差分不等式 $ pre[s-1]-pre[t]==w $ 为了满足松弛操作 ...
- CF 208E. Blood Cousins [dsu on tree 倍增]
题意:给出一个森林,求和一个点有相同k级祖先的点有多少 倍增求父亲然后和上题一样还不用哈希了... #include <iostream> #include <cstdio> ...