一、前言

Hi,大家好,我是承香墨影!

当我们需要发布一款 App 到应用市场的时候,一般需要我们针对不同的市场生产不同的渠道包,它们使用的是同一套代码,只是会包含一些各自的渠道信息,用于我们做数据分析。

前几天,企鹅电竞团队开源了自己的 Android Apk 多渠道打包工具:VasDolly,比美团的 Walle 更全面一些。

正好借这个机会,来讲解一下 Android 的不同版本的签名机制的差异。

二、Android 的签名

2.1 应用签名

通过对 Apk 进行签名,开发者可以证明对 Apk 的所有权和控制权,可用于安装和更新其应用。而在 Android 设备上的安装 Apk ,如果是一个没有被签名的 Apk,则会被拒绝安装。

在安装 Apk 的时候,软件包管理器也会验证 Apk 是否已经被正确签名,并且通过签名证书和数据摘要验证是否合法没有被篡改。只有确认安全无篡改的情况下,才允许安装在设备上。

简单来说,APK 的签名主要作用有两个:

  1. 证明 APK 的所有者。
  2. 允许 Android 市场和设备校验 APK 的正确性。

而在 Android 中,支持两种应用签名的方案:

  1. 基于 JAR 签名方案(v1 方案)。
  2. 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 个部分。

  1. ZIP 条目的内容。
  2. APK 签名分块(APK Signing Block)。
  3. ZIP 中央目录。
  4. 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 的版本之后,会导致解包失败。

所以我们在想,一个高效的多渠道打包方案,有几个关键单需要注意。

  1. 不能破坏签名。
  2. 不能重新打包。
  3. 读取信息,必须高效。

不破坏签名就限制了不能解包以及重新签名,势必对效率有所提高。

而除了 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

今天在公众号后台回复成长『成长』,将会得到我整理的一些学习资料,也能回复『加群』,一起学习进步。

推荐阅读:

借腾讯开源 VasDolly,谈谈 Android 签名和多渠道打包的原理!的更多相关文章

  1. Android Studio下多渠道打包

    Android Studio下实现多渠道打包 直接上步骤 步骤 1. 清单文件添加属性(以友盟统计为例) 在application标签下添加meta-data属性 <application -- ...

  2. android 7.0 多渠道打包 - 美团开源工具Walle 命令行打包

    在Android 7.0(Nougat)推出了新的应用签名方案APK Signature Scheme v2后,之前快速生成渠道包的方式(美团Android自动化之旅-生成渠道包)已经行不通了,对此美 ...

  3. android 签名、混淆打包

    1.android 签名 使用eclipse导出带签名的apk,最简单的方式. 最后一步finish ,就能导出一个xxx.keystore的文件了. 下次再发布新版本的时候,使用这个生成的签名继续使 ...

  4. 【Android Studio探索之路系列】之十:Gradle项目构建系统(四):Android Studio项目多渠道打包

    作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.co ...

  5. Android几种多渠道打包

    1.什么是多渠道打包 在不同的应用市场可能有不同的统计需求,需要为每个应用市场发布一个安装包,这里就引出了Android的多渠道打包.在安装包中添加不同的标识,以此区分各个渠道,方便统计app在市场的 ...

  6. Android 使用 Gradle 多渠道打包

    安卓开发完毕.对于一个开放应用而言,我们须要公布到不同的应用市场,同一时候我们也须要统计不同市场的用户下载量. (通过启动应用后获取不同市场apk中的不同值来区分) 以下用一个详细的实例来说明: 1. ...

  7. Android Studio + gradle多渠道打包

    通过工具栏的Build->Build Apk 好像只能打包第一个Module(eclipse里面是Project的概念),怎么多渠道打包呢?目前好像只能一个一个的打 首先在清单文件里设置个变量: ...

  8. 【腾讯Bugly干货分享】Android 新一代多渠道打包神器

    关于作者: 李涛,腾讯Android工程师,14年加入腾讯SNG增值产品部,期间主要负责手Q动漫.企鹅电竞等项目的功能开发和技术优化.业务时间喜欢折腾新技术,写一些技术文章,个人技术博客:www.lt ...

  9. Android 新一代多渠道打包神器

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:李涛 ApkChannelPackage是一种快速多渠道打包工具,同时支持基于V1签名和V2签名进行多渠 ...

随机推荐

  1. Java数据库基础(JDBC)

    JDBC(Java Data Base Connectivity):SUN公司为了简化统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC: 这样应用程序就不需要关注数据库底层的详细 ...

  2. 基于 HTML5 WebGL 的 3D 仪表数据监控

    工控仪表重点发展基于现场总线技术的主控系统装置及智能化仪表.特种和专用自动化仪表:全面扩大服务领域,推进仪器仪表系统的数字化.智能化.网络化,完成 自动化仪表从模拟技术向数字技术的转变:推进具有自主版 ...

  3. java中的按位与运算

    package scanner; public class SingleAnd { public static void main(String[] args) { int[] first = {10 ...

  4. list类型

    list是一个链表结构,可以模拟栈,队列 lpush list中压入一个元素   模拟栈 lrange  取出list的中的元素(0 -1 表示从头取到尾) rpush list中压入一个元素   模 ...

  5. JVM-类的四种载入方式

    package org.burning.sport.javase.classloader; public class ClassLoaderMain { public static void main ...

  6. python编程理念

    在python控制台输入import this之后输出如下: The Zen of Python, by Tim PetersBeautiful is better than ugly.Explici ...

  7. python3 第二章 - 第一个程序

    1、安装 打开官网 https://www.python.org/downloads/ 下载python3.6.4 如果你是windows\mac电脑,直接双击安装包,一路next即可,如果你是lin ...

  8. 更改Patrol Agent的密码

    大家可以使用P3console去做,具体方法请见:http://wenku.baidu.com/link?url=HbSzxNV2SPrlpk_Bfmcg0CNZuAlyX4jgdp4vbrxmynv ...

  9. 函数式编程--lambda表达式对比匿名内部类

    从前面的整理中我们看出了,Lambda表达式其实是匿名内部类的一种简化,因此它可以部分取代匿名内部类. 1,Lambda表达式与匿名内部类存在如下相同点: 1),Lambda表达式与匿名内部类一样,都 ...

  10. Java语言程序设计基础篇第10版第5章习题答案

    5.1 1 public class Demo { public static void main(String[] args) { // 创建一个输入对象 java.util.Scanner inp ...