美文网首页android开发
App 的编译和打包流程

App 的编译和打包流程

作者: 凯玲之恋 | 来源:发表于2020-10-28 15:30 被阅读0次

    1、APK 的组成

    我们都知道,APK 其实是一个 zip 类型的压缩包,而一个典型的 APK 通常都会包含了以下七部分的内容:
    我们都知道,APK 其实是一个 zip 类型的压缩包,而一个典型的 APK 通常都会包含了以下七部分的内容:

    • 1、AndroidManifest.xml:如果 App 是一本书,那么这个文件就是它的 “封面” 和 “目录” 。它记载了 App 的名称、权限声明、所包含的组件等一系列信息。

    • 2、classes.dex:它是由项目源码生成的 .class 文件经过进一步地转换而生成的 Android 系统可识别的 Dalvik Byte Code。并且,由于 Android 系统中的字节码和标准 JVM 中的字节码是有区别的,所以如果 App 中引用了第三方 jar 包的话,那么通常情况下它也会被包含在 classes.dex 中。

    • 3、resources.arsc:资源索引表,包含编译后的二进制资源文件。每当在 res 文件夹下放一个文件时,aapt 就会自动生成对应的 id 并保存在 .R 文件中,但 .R 文件仅仅只是保证编译程序不会报错,实际上在应用运行时,系统会根据 ID 寻找对应的资源路径,而 resources.arsc 文件就是用来记录这些 ID 和 资源文件位置对应关系 的文件。

    • 4、res 目录:未编译的资源文件。

    • 5、asserts:额外建立的资源文件夹。res 和 assets 的不同在于 res 目录下的文件会在 .R 文件中生成对应的资源 ID,而 assets 不会自动生成对应的 ID,而是通过 AssetManager 类的接口来获取。

    • 6、libs 目录:如果存在的话,存放的是 ndk 编出来的 so 库 。

    • 7、META-INF 目录:用于保存 App 的签名和校验信息,以保证程序的完整性。当生成 APK 包时,系统会对包中的所有内容做一次校验,然后将结果保存在这里。而手机在安装这一 App 时还会对内容再做一次校验,并和 META-INF 中的值进行比较,以避免 APK 被恶意篡改。其中包含如下 三个文件,如下所示:

      • 1)、MANIFEST.MF:其中每一个资源文件都有一个对应的 SHA-256-Digest(SHA1) 签名,MANIFEST.MF 文件的 SHA256(SHA1) 经过 base64 编码的结果即为 CERT.SF 中的 SHA256(SHA1)-Digest-Manifest 值。
      • 2)、CERT.SF:除了开头处定义的 SHA256(SHA1)-Digest-Manifest 值,后面几项的值是对 MANIFEST.MF 文件中的每项再次 SHA256(SHA1) 经过 base64 编码后的值。
      • 3)、CERT.RSA:其中包含了公钥、加密算法等信息。首先,对前一步生成的 CERT.SF 使用了 SHA256(SHA1)生成了数字摘要并使用了 RSA 加密,接着,利用了开发者私钥进行签名。然后,在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。

    2、APK 的编译打包流程

    APK 打包流程图
    打包流程可简述为如下 八个步骤:
    处理.aidl file
    1、首先,.aidl(Android Interface Description Language)文件需要通过 aidl 工具转换成编译器能够处理的 Java 接口文件。
    打包资源文件AAPT
    2、同时,资源文件(包括 AndroidManifest.xml、布局文件、各种 xml 资源等等)将被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及之后使用 AAPT2 替代了 AAPT)处理为最终的 resources.arsc,并生成 R.java 文件以保证源码编写时可以方便地访问到这些资源。
    编译Compiler
    3、然后,通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,最终它们会统一被编译成 .class 文件。

    生成dex文件
    4、因为 .class 并不是 Android 系统所能识别的格式,所以还需要通过 dex 工具将它们转化为相应的 Dalvik 字节码(包含压缩常量池以及清除冗余信息等工作)。这个过程中还会加入应用所依赖的所有 “第三方库”
    ApkBuilder 生成未签名apk
    5、下一步,通过 ApkBuilder 工具将资源文件、DEX 文件打包生成 APK 文件。
    Jarsigner签名
    7、然后,通过签名工具 Jarsigner 或者其它签名工具对 APK 进行签名得到签名后的 APK。如果是在 Debug 模式下,签名所用的 keystore 是系统自带的默认值,否则我们需要提供自己的私钥以完成签名过程。
    ZipAlign对齐
    8、最后,如果是正式版的 APK,还会利用 ZipAlign 工具进行对齐处理,以提高程序的加载和运行速度。而对齐的过程就是将 APK 文件中所有的资源文件距离文件的起始位置都偏移4字节的整数倍,这样通过 mmap 访问 APK 文件的速度会更快,并且会减少其在设备上运行时的内存占用。

    为什么 XML 资源文件要从文本格式编译成二进制格式?

    主要基于以下 两点原因:

    • 1、空间占用更小:因为所有 XML 元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中,并且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而可以减少文件的大小。
    • 2、解析效率更高:二进制格式的 XML 文件解析速度更快。这是由于二进制格式的 XML 元素里面不再包含有字符串值,因此就避免了进行字符串解析,从而提高了解析效率。

    Android 资源管理框架又是如何快速定位到最匹配资源的?

    主要基于两个文件,如下所示:

    • 1、资源 ID 文件 R.java:赋予每一个非 assets 资源一个 ID 值,这些 ID 值以常量的形式定义在 R.java 文件中。
    • 2、资源索引表 resources.arsc:用来描述那些具有 ID 值的资源的配置信息。

    3、签名算法的原理

    什么是签名?

    在 Apk 中写入一个 “指纹”。指纹写入以后,Apk 中有任何修改,都会导致这个指纹无效,Android 系统在安装 Apk 进行签名校验时就会不通过,从而保证了安全性。

    那么,为什么要签名?
    主要有 两点原因,如下所示:

    • 1、确保 Apk 来源的真实性。
    • 2、确保 Apk 没有被第三方篡改。

    数字摘要

    对一个任意长度的数据,通过一个 Hash 算法计算后,都可以得到一个固定长度的二进制数据,这个数据就称为 “摘要”。

    在签名和校验的流程之中,应用了许多密码学的知识,这里我们需要先大致了解一下。

    Hash(散列算法)的基础原理

    Hash 算法就是 将数据(如一段文字)运算变为另一固定长度值。它的特点主要有如下 三点:

    • 1、唯一性。
    • 2、固定长度:比较常用的 Hash 算法有 MD5 和 SHA1,MD5 的长度是128位,SHA1 的长度是160位。
    • 3、不可逆性。
      而常用的 Hash 算法有如下 三种:
    • 1、SHA-1:在密码学中,SHA-1(安全散列算法1)是一种加密散列函数,它接受输入并产生一个160 位(20 字节)散列值,称为消息摘要。
    • 2、MD5:MD5 消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
    • 3、SHA-2:名称来自于安全散列算法2(Secure Hash Algorithm 2)的缩写,一种密码散列函数算法标准,其下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

    签名和校验的主要过程

    签名就是 在摘要的基础上再进行一次加密,对摘要加密后的数据就可以当作数字签名。
    签名过程:
    签名过程可以细分为 三步,如下所示:

    • 1、计算摘要:通过 Hash 算法提取出原始数据的摘要。
    • 2、计算签名:再通过基于密钥(私钥)的非对称加密算法对提取出的摘要进行加密,加密后的数据就是签名信息。
    • 3、写入签名:将签名信息写入原始数据的签名区块内。
      校验过程:
      校验过程同样也可以分为 三步,如下:
    • 1、提取摘要:首先用同样的 Hash 算法从接收到的数据中提取出摘要。
    • 2、解密签名:使用发送方的公钥对数字签名进行解密,解密出原始摘要。
    • 3、比较摘要:如果解密后的数据和提取的摘要一致,则校验通过;如果数据被第三方篡改过,解密后的数据和摘要将会不一致,则校验不通过。
      那么,我们该如何保证公钥的可靠性呢?答案是 数字证书。

    数字证书

    数字证书是 身份认证机构(Certificate Authority)颁发的,主要包含了以下 六类信息:

    • 1、证书颁发机构
    • 2、证书颁发机构签名
    • 3、证书绑定的服务器域名
    • 4、证书版本、有效期
    • 5、签名使用的加密算法(非对称算法,如 RSA)
    • 6、公钥等

    接收方收到消息后,需要先向 CA 验证证书的合法性,再进行签名校验。

    需要注意的是,Apk 的证书通常是自签名的,也就是由开发者自己制作,没有向 CA 机构申请。Android 在安装 Apk 时并没有校验证书本身的合法性,只是从证书中提取公钥和加密算法,这也正是对第三方 Apk 重新签名后,还能够继续在没有安装这个 Apk 的系统中继续安装的原因

    keystore 和证书格式

    keystore 文件中包含了 私钥、公钥和数字证书。根据编码不同,keystore 文件分为很多种,Android 使用的是 Java 标准 keystore 格式 JKS(Java Key Storage),所以通过 Android Studio 导出的 keystore 文件是以 .jks 结尾的。

    keystore 使用的 证书标准是 X.509,X.509 标准也有多种 编码格式,常用的有两种:pem(Privacy Enhanced Mail)和 der(Distinguished Encoding Rules)。jks 使用的是 der 格式,但是,Android 也支持直接使用 pem 格式的证书进行签名。

    下面,我们了解下两种证书编码格式的区别,如下所示:

    • DER(Distinguished Encoding Rules):二进制格式,所有类型的证书和私钥都可以存储为 der 格式。
    • PEM(Privacy Enhanced Mail):base64 编码,内容以-----BEGIN xxx----- 开头,以-----END xxx----- 结尾。

    jarsigner 和 apksigner 的区别

    Android 提供了 两种对 Apk 的签名方式,一种是基于 JAR 的签名方式,另一种是基于 Apk 的签名方式,它们的 主要区别在于使用的签名文件不一样:jarsigner 使用 keystore 文件进行签名;而 apksigner 除了支持使用 keystore 文件进行签名外,还支持直接指定 pem 证书文件和私钥进行签名。

    在我们签名时,除了要指定 keystore 文件和密码外,也要指定 alias 和 key 的密码,这是为什么呢?

    keystore 是一个密钥库,也就是说它可以存储多对密钥和证书,keystore 的密码是用于保护 keystore 本身的,每一对密钥和证书是通过 alias 来区分的。所以 jarsigner 是支持使用多个证书对 Apk 进行签名的,apksigner 也同样支持。

    Android Apk V1 验证签名的原理

    Android Apk V1 验证签名的过程主要可以分为如下 四步:

    • 1、解析出 CERT.RSA 文件中的证书、公钥,解密 CERT.RSA 中的加密数据。
    • 2、解密结果和 CERT.SF 的指纹进行对比,保证 CERT.SF 没有被篡改。
    • 3、接着,将 CERT.SF 中的内容再和 MANIFEST.MF 中的指纹对比,保证 MANIFEST.MF 文件没有被篡改。
    • 4、MANIFEST.MF 中的内容和 APK 所有文件指纹逐一对比,保证 APK 没有被篡改。

    参考

    深入探索编译插桩技术(一、编译基础)

    相关文章

      网友评论

        本文标题:App 的编译和打包流程

        本文链接:https://www.haomeiwen.com/subject/iwpqvktx.html