App 的編譯和打包流程

1收奔、APK 的組成

我們都知道给僵,APK 其實(shí)是一個(gè) zip 類型的壓縮包烙无,而一個(gè)典型的 APK 通常都會(huì)包含了以下七部分的內(nèi)容:
我們都知道锋谐,APK 其實(shí)是一個(gè) zip 類型的壓縮包,而一個(gè)典型的 APK 通常都會(huì)包含了以下七部分的內(nèi)容:

  • 1截酷、AndroidManifest.xml:如果 App 是一本書涮拗,那么這個(gè)文件就是它的 “封面” 和 “目錄” 。它記載了 App 的名稱迂苛、權(quán)限聲明多搀、所包含的組件等一系列信息。

  • 2灾部、classes.dex:它是由項(xiàng)目源碼生成的 .class 文件經(jīng)過進(jìn)一步地轉(zhuǎn)換而生成的 Android 系統(tǒng)可識別的 Dalvik Byte Code康铭。并且,由于 Android 系統(tǒng)中的字節(jié)碼和標(biāo)準(zhǔn) JVM 中的字節(jié)碼是有區(qū)別的赌髓,所以如果 App 中引用了第三方 jar 包的話从藤,那么通常情況下它也會(huì)被包含在 classes.dex 中。

  • 3锁蠕、resources.arsc:資源索引表夷野,包含編譯后的二進(jìn)制資源文件。每當(dāng)在 res 文件夾下放一個(gè)文件時(shí)荣倾,aapt 就會(huì)自動(dòng)生成對應(yīng)的 id 并保存在 .R 文件中悯搔,但 .R 文件僅僅只是保證編譯程序不會(huì)報(bào)錯(cuò),實(shí)際上在應(yīng)用運(yùn)行時(shí)舌仍,系統(tǒng)會(huì)根據(jù) ID 尋找對應(yīng)的資源路徑妒貌,而 resources.arsc 文件就是用來記錄這些 ID 和 資源文件位置對應(yīng)關(guān)系 的文件。

  • 4铸豁、res 目錄:未編譯的資源文件灌曙。

  • 5、asserts:額外建立的資源文件夾节芥。res 和 assets 的不同在于 res 目錄下的文件會(huì)在 .R 文件中生成對應(yīng)的資源 ID在刺,而 assets 不會(huì)自動(dòng)生成對應(yīng)的 ID,而是通過 AssetManager 類的接口來獲取头镊。

  • 6蚣驼、libs 目錄:如果存在的話,存放的是 ndk 編出來的 so 庫 相艇。

  • 7颖杏、META-INF 目錄:用于保存 App 的簽名和校驗(yàn)信息,以保證程序的完整性厂捞。當(dāng)生成 APK 包時(shí)输玷,系統(tǒng)會(huì)對包中的所有內(nèi)容做一次校驗(yàn),然后將結(jié)果保存在這里靡馁。而手機(jī)在安裝這一 App 時(shí)還會(huì)對內(nèi)容再做一次校驗(yàn)欲鹏,并和 META-INF 中的值進(jìn)行比較,以避免 APK 被惡意篡改臭墨。其中包含如下 三個(gè)文件赔嚎,如下所示:

    • 1)、MANIFEST.MF:其中每一個(gè)資源文件都有一個(gè)對應(yīng)的 SHA-256-Digest(SHA1) 簽名胧弛,MANIFEST.MF 文件的 SHA256(SHA1) 經(jīng)過 base64 編碼的結(jié)果即為 CERT.SF 中的 SHA256(SHA1)-Digest-Manifest 值尤误。
    • 2)、CERT.SF:除了開頭處定義的 SHA256(SHA1)-Digest-Manifest 值结缚,后面幾項(xiàng)的值是對 MANIFEST.MF 文件中的每項(xiàng)再次 SHA256(SHA1) 經(jīng)過 base64 編碼后的值损晤。
    • 3)、CERT.RSA:其中包含了公鑰红竭、加密算法等信息尤勋。首先,對前一步生成的 CERT.SF 使用了 SHA256(SHA1)生成了數(shù)字摘要并使用了 RSA 加密茵宪,接著最冰,利用了開發(fā)者私鑰進(jìn)行簽名。然后稀火,在安裝時(shí)使用公鑰解密暖哨。最后,將其與未加密的摘要信息(MANIFEST.MF文件)進(jìn)行對比凰狞,如果相符篇裁,則表明內(nèi)容沒有被修改。

Gradle Task

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:compressDebugAssets UP-TO-DATE
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:checkDebugDuplicateClasses UP-TO-DATE
> Task :app:desugarDebugFileDependencies UP-TO-DATE
> Task :app:mergeExtDexDebug UP-TO-DATE
> Task :app:mergeLibDexDebug UP-TO-DATE
> Task :app:dexBuilderDebug UP-TO-DATE
> Task :app:mergeProjectDexDebug UP-TO-DATE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:packageDebug UP-TO-DATE

> Task :app:installDebug
//aidl 轉(zhuǎn)換aidl文件為java文件
> Task :app:compileDebugAidl

//生成BuildConfig文件
> Task :app:generateDebugBuildConfig

//獲取gradle中配置的資源文件
> Task :app:generateDebugResValues

// merge資源文件
> Task :app:mergeDebugResources

// merge assets文件
> Task :app:mergeDebugAssets
> Task :app:compressDebugAssets

// merge所有的manifest文件
> Task :app:processDebugManifest

//AAPT 生成R文件
> Task :app:processDebugResources

//編譯kotlin文件
> Task :app:compileDebugKotlin

//javac 編譯java文件
> Task :app:compileDebugJavaWithJavac

//轉(zhuǎn)換class文件為dex文件
> Task :app:dexBuilderDebug

//打包成apk并簽名
> Task :app:packageDebug

生成BuildConfig文件赡若,資源文件
在引入Gradle編譯工具之后茴恰,Apk的打包流程就多了這么一步,生成BuildConfig文件和資源文件斩熊。

也就是會(huì)根據(jù)build.gradle里面配置的內(nèi)容生成相應(yīng)的java代碼或者res代碼往枣。簡單舉個(gè)例子:

 //build.gradle
    buildTypes {
        debug{
            buildConfigField("boolean", "ISDEBUG", "true")
            resValue "string", "TestName", "love1"
        }
        release {
            buildConfigField("boolean", "ISDEBUG", "false")
            resValue "string", "TestName", "love2"
        }
    }

    //BuildConfig.java
    public final class BuildConfig {
    // Field from build type: debug
    public static final boolean ISDEBUG = true;
 }

 R.string.TestName

merge 資源文件
這一步就是合并res資源文件,assets文件粉渠,manifest文件分冈。

因?yàn)樵陧?xiàng)目中會(huì)依賴不同的庫、組件霸株,也會(huì)有多渠道的需求雕沉,所以merge這一步操作就是將不同地方的資源文件進(jìn)行整合。

多個(gè)manifest文件需要整理成一個(gè)完整的文件去件,所以如果有屬性沖突這一步就會(huì)報(bào)錯(cuò)坡椒。資源文件也會(huì)整理分類到不同的分辨率目錄中扰路。

AAPT/AAPT2(打包資源文件)

2、APK 的編譯打包流程

APK 打包流程圖

打包流程可簡述為如下 八個(gè)步驟:

處理.aidl file

1倔叼、首先汗唱,.aidl(Android Interface Description Language)文件需要通過 aidl 工具轉(zhuǎn)換成編譯器能夠處理的 Java 接口文件。

打包資源文件AAPT

AAPT丈攒,全稱Android Asset Packaging Tool哩罪,所以這個(gè)構(gòu)建工具就是用來打包資源文件的。

資源文件包括:圖片巡验,res目錄下的xml文件际插,AndroidManifest.xml文件;

處理資源文件主要包括兩步

  • 1显设、編譯:將資源文件編譯為二進(jìn)制格式框弛。
    把所有的Android資源文件進(jìn)行解析,生成擴(kuò)展名為.flat的二進(jìn)制文件捕捂。比如是png圖片功咒,那么就會(huì)被壓縮處理,采用.png.flat的擴(kuò)展名绞蹦。

  • 2力奋、鏈接:合并所有已編譯的文件并打包到一個(gè)軟件包中。
    首先幽七,這一步會(huì)生成輔助文件景殷,比如R.java(R文件),R文件大家應(yīng)該都比較熟悉澡屡,就是一個(gè)資源索引文件猿挚,我們平時(shí)引用也都是通過R.的方式引用資源id。

最后驶鹉,會(huì)將R文件和之前的二進(jìn)制文件進(jìn)行打包绩蜻,打包到一個(gè)APK壓縮包(沒有dex文件、沒有簽名)室埋。

2办绝、同時(shí)姚淆,資源文件(包括 AndroidManifest.xml、布局文件降淮、各種 xml 資源等等)將被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及之后使用 AAPT2 替代了 AAPT)處理為最終的 resources.arsc,并生成 R.java 文件以保證源碼編寫時(shí)可以方便地訪問到這些資源搏讶。

Android Gradle插件 3.0.0 及更高版本默認(rèn)情況下會(huì)啟用 AAPT2佳鳖,而老版本的AAPT已經(jīng)被棄用系吩,那么AAPT2到底優(yōu)化改進(jìn)了什么呢来庭?

  • 1、鏈接過程優(yōu)化
    在AAPT中是沒有鏈接功能的淑玫,會(huì)將所有的資源進(jìn)行編譯生成壓縮包巾腕。這樣處理方式有個(gè)缺點(diǎn)就是每次編譯都要全量編譯。

所以在AAPT2中用到鏈接的功能叁鉴,當(dāng)修改了某個(gè)資源文件之后幌墓,只需要重新編譯這個(gè)改變的文件,然后與其他資源進(jìn)行鏈接即可常侣,支持了增量更新胳施,大大提升了效率。

  • 2焦辅、行為變化
    對一些行為進(jìn)行了優(yōu)化椿胯,一些錯(cuò)誤的元素以前不會(huì)報(bào)錯(cuò),只會(huì)警告或者忽略前方,現(xiàn)在會(huì)直接報(bào)錯(cuò)廉油,保證程序正確運(yùn)行娱两。比如

1)、在以前的AAPT版本十兢,Android 清單文件中出現(xiàn)錯(cuò)誤的節(jié)點(diǎn)元素只會(huì)被忽略或警告,而AAPT2開始會(huì)對這些節(jié)點(diǎn)進(jìn)行報(bào)錯(cuò),比如:

<activity android:name=".MainActivity">
     <action android:name="android.intent.action.TEST" />
</activity>

AndroidManifest.xml:15: error: unknown element <action> found.

2)卫袒、在AAPT2中单匣,無法通過name屬性指明資源類型了,需要單獨(dú)使用type屬性:

    <item name="attr/my_attr">@color/pink</item>
    // 修改為
    <item type="attr" name="my_attr">@color/pink</item>

3)码秉、ForegroundLinearLayout(前景色相關(guān))屬性限制嚴(yán)格

foregroundInsidePadding屬性转砖,不屬于android命名空間鲸伴,所以AAPT2的改進(jìn)就是對于這個(gè)屬性使用更加嚴(yán)格了,原來使用android:foregroundInsidePadding的時(shí)候會(huì)被忽略姓赤,現(xiàn)在會(huì)報(bào)錯(cuò)不铆,需要改為foregroundInsidePadding蜘矢。

4)、@ 資源引用符號使用嚴(yán)格

對于遺漏或者錯(cuò)誤引用@(資源引用符號)時(shí)候岖食,AAPT2會(huì)報(bào)錯(cuò)舞吭。

5)羡鸥、庫配置不正確

當(dāng)某些庫創(chuàng)建過程中R文件字段聲明為final會(huì)導(dǎo)致報(bào)錯(cuò),AAPT2就會(huì)對這種情況進(jìn)行優(yōu)化存和。

編譯Compiler javac(編譯java文件)

3、然后纵朋,通過 Java Compiler 編譯 R.java茄袖、Java 接口文件宪祥、Java 源文件,最終它們會(huì)統(tǒng)一被編譯成 .class 文件藏澳。

接下來就是編譯java文件了肘交,用到的工具就是大家熟知的javac涯呻,通過它將java文件編譯成.class文件腻要。

注解代碼也是在這個(gè)階段生成的。當(dāng)注解的生命周期被設(shè)置為CLASS的時(shí)候效诅,就代表該注解會(huì)在編譯class文件的時(shí)候生效乱投,并且存在與java源文件和Class字節(jié)碼文件顷编。

javac的基礎(chǔ)命令還是可以了解下:

javac -d destdir(class文件存放目錄) srcFile(java文件)

生成dex文件 dx/r8/d8 (編譯class文件)

4、因?yàn)?.class 并不是 Android 系統(tǒng)所能識別的格式双肤,所以還需要通過 dex 工具將它們轉(zhuǎn)化為相應(yīng)的 Dalvik 字節(jié)碼(包含壓縮常量池以及清除冗余信息等工作)茅糜。這個(gè)過程中還會(huì)加入應(yīng)用所依賴的所有 “第三方庫”

再談?wù)勥@三個(gè)工具(dx/r8/d8)的區(qū)別:

  • dx是最早的轉(zhuǎn)換工具素挽,用于轉(zhuǎn)換class文件為dex文件。
  • Android Studio 3.1之后缩赛,引入了D8編譯器和 R8 工具峦筒。

注意這里的措辭:D8 編譯器和 R8 工具。

所以D8就是用來代替dx用來進(jìn)行轉(zhuǎn)換class文件的卤材,它的優(yōu)勢在于:編譯更快峦失、更小的dex文件尉辑、更好的性能。

而R8工具是用來替代ProGuard的卓练,用于代碼的壓縮和混淆购啄。

編譯class文件過程也常用于編譯插樁狮含,比如ASM,通過直接操作字節(jié)碼文件完成代碼修改或生成蔚龙。

apkbuilder/zipflinger(生成APK包)

5映胁、這一步就是生成APK文件屿愚,將manifest文件、resources文件穷遂、dex文件娱据、assets文件等等打包成一個(gè)壓縮包,也就是apk文件忌穿。

在老版本使用的工具是apkbuilder掠剑,但是在最新的版本我發(fā)現(xiàn)沒有這個(gè)工具了,sdk目錄下也找不到了井佑。

所以我想到從打包的task——packageDebug中找找答案躬翁,果然盯拱,讓我找到了新的打包工具——zipflinger。

//PackageAndroidArtifact.java (packageDebug相關(guān)代碼)
for (File arch : archives) {
    mApkCreator.writeZip(arch, pathNameMap::get, name -> !names.contains(name));
}

mApkCreator =new ApkFlinger(mCreationData, compressionLevel, !mIsDebuggableBuild);

/** An implementation of [ApkCreator] using the zipflinger library */
class ApkFlinger

jarsigner/apksigner(簽名)

在生成APK文件之后,必須對該apk文件進(jìn)行簽名甚侣,否則無法被安裝殷费。

之前大家比較熟知的簽名工具是JDK提供的jarsigner低葫,而apksigner是Google專門為Android提供的簽名和簽證工具嘿悬。

其區(qū)別就在于jarsigner只能進(jìn)行v1簽名,而apksigner可以進(jìn)行v2窒盐、v3钢拧、v4簽名源内。

7、然后嗽交,通過簽名工具 Jarsigner 或者其它簽名工具對 APK 進(jìn)行簽名得到簽名后的 APK。如果是在 Debug 模式下拾枣,簽名所用的 keystore 是系統(tǒng)自帶的默認(rèn)值放前,否則我們需要提供自己的私鑰以完成簽名過程糯彬。

  • v1簽名
    v1簽名方式主要是利用META-INFO文件夾中的三個(gè)文件撩扒。

首先,將apk中除了META-INFO文件夾中的所有文件進(jìn)行進(jìn)行摘要寫到 META-INFO/MANIFEST.MF炒辉;然后計(jì)算MANIFEST.MF文件的摘要寫到CERT.SF黔寇;最后計(jì)算CERT.SF的摘要斩萌,使用私鑰計(jì)算簽名颊郎,將簽名和開發(fā)者證書寫到CERT.RSA。

所以META-INFO文件夾中這三個(gè)文件就能保證apk不會(huì)被修改榛做。
但是缺點(diǎn)也很明顯内狸,META-INFO文件夾不會(huì)被簽名昆淡,所以美團(tuán)針對這種簽名方式設(shè)計(jì)了一種多渠道打包方案:

利用pythone在META-INFO文件夾中創(chuàng)建一個(gè)文件,其名稱就是渠道名瘪撇,然后用java去讀取文件名獲取渠道。

  • v2簽名
    Android7.0之后鹏氧,推出了v2簽名佩谣,為了解決v1簽名速度慢以及簽名不完整的問題茸俭。

apk本質(zhì)上是一個(gè)壓縮包,而壓縮包文件格式一般分為三塊:

文件數(shù)據(jù)區(qū)艇炎,中央目錄結(jié)果腾窝,中央目錄結(jié)束節(jié)虹脯。

而v2要做的就是循集,在文件中插入一個(gè)APK簽名分塊,位于中央目錄部分之前疆柔,如下圖:


這樣處理之后婆硬,文件就完成無法修改了奸例。

  • v3簽名

Android 9 推出了v3簽名方案查吊,和v2簽名方式基本相同湖蜕,不同的是在v3簽名分塊中添加了有關(guān)受支持的sdk版本和新舊簽名信息昭抒,可以用作簽名替換升級炼杖。

  • v4簽名
    Android 11 推出了v4簽名方案坤邪。

v4 簽名基于根據(jù) APK 的所有字節(jié)計(jì)算得出的 Merkle 哈希樹艇纺。它完全遵循 fs-verity 哈希樹的結(jié)構(gòu)邮弹,將簽名存儲(chǔ)在單獨(dú)的.apk.idsig 文件中腌乡。

ZipAlign對齊

zipalign 是一種歸檔對齊工具,可對 Android 應(yīng)用 (APK) 文件提供重要的優(yōu)化
它會(huì)使 APK 中的所有未壓縮數(shù)據(jù)(例如圖片或原始文件)在 4 字節(jié)邊界上對齊捞高。
這里涉及到一個(gè)Data structurealignment(數(shù)據(jù)對齊)的知識點(diǎn)硝岗,其大概意思就是如果數(shù)據(jù)是自然對齊的型檀,CPU讀寫就會(huì)更高效听盖。

簽名工具的不同帶來的對齊處理的順序不同

如果使用的是 apksigner皆看,只能在為 APK 文件簽名之前執(zhí)行 zipalign。
如果使用的是 jarsigner无埃,只能在為 APK 文件簽名之后執(zhí)行 zipalign嫉称。

8灵疮、最后震捣,如果是正式版的 APK,還會(huì)利用 ZipAlign 工具進(jìn)行對齊處理润樱,以提高程序的加載和運(yùn)行速度祥国。而對齊的過程就是將 APK 文件中所有的資源文件距離文件的起始位置都偏移4字節(jié)的整數(shù)倍,這樣通過 mmap 訪問 APK 文件的速度會(huì)更快啊犬,并且會(huì)減少其在設(shè)備上運(yùn)行時(shí)的內(nèi)存占用觉至。

為什么 XML 資源文件要從文本格式編譯成二進(jìn)制格式睡腿?

主要基于以下 兩點(diǎn)原因:

  • 1席怪、空間占用更小:因?yàn)樗?XML 元素的標(biāo)簽挂捻、屬性名稱、屬性值和內(nèi)容所涉及到的字符串都會(huì)被統(tǒng)一收集到一個(gè)字符串資源池中骨田,并且會(huì)去重态贤。有了這個(gè)字符串資源池醋火,原來使用字符串的地方就會(huì)被替換成一個(gè)索引到字符串資源池的整數(shù)值胎撇,從而可以減少文件的大小晚树。
  • 2雅采、解析效率更高:二進(jìn)制格式的 XML 文件解析速度更快。這是由于二進(jìn)制格式的 XML 元素里面不再包含有字符串值刑棵,因此就避免了進(jìn)行字符串解析愚铡,從而提高了解析效率沥寥。

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

主要基于兩個(gè)文件,如下所示:

  • 1捧书、資源 ID 文件 R.java:賦予每一個(gè)非 assets 資源一個(gè) ID 值经瓷,這些 ID 值以常量的形式定義在 R.java 文件中舆吮。
  • 2廊营、資源索引表 resources.arsc:用來描述那些具有 ID 值的資源的配置信息露筒。

3慎式、簽名算法的原理

什么是簽名?

在 Apk 中寫入一個(gè) “指紋”癣防。指紋寫入以后蕾盯,Apk 中有任何修改级遭,都會(huì)導(dǎo)致這個(gè)指紋無效挫鸽,Android 系統(tǒng)在安裝 Apk 進(jìn)行簽名校驗(yàn)時(shí)就會(huì)不通過,從而保證了安全性盔沫。

那么架诞,為什么要簽名婿牍?
主要有 兩點(diǎn)原因等脂,如下所示:

  • 1上遥、確保 Apk 來源的真實(shí)性。
  • 2辣恋、確保 Apk 沒有被第三方篡改伟骨。

數(shù)字摘要

對一個(gè)任意長度的數(shù)據(jù)携狭,通過一個(gè) Hash 算法計(jì)算后逛腿,都可以得到一個(gè)固定長度的二進(jìn)制數(shù)據(jù),這個(gè)數(shù)據(jù)就稱為 “摘要”仅颇。

在簽名和校驗(yàn)的流程之中单默,應(yīng)用了許多密碼學(xué)的知識,這里我們需要先大致了解一下忘瓦。

Hash(散列算法)的基礎(chǔ)原理

Hash 算法就是 將數(shù)據(jù)(如一段文字)運(yùn)算變?yōu)榱硪还潭ㄩL度值搁廓。它的特點(diǎn)主要有如下 三點(diǎn):

  • 1、唯一性。
  • 2枚抵、固定長度:比較常用的 Hash 算法有 MD5 和 SHA1线欲,MD5 的長度是128位汽摹,SHA1 的長度是160位逃延。
  • 3煎饼、不可逆性。
    而常用的 Hash 算法有如下 三種:
  • 1告组、SHA-1:在密碼學(xué)中舟舒,SHA-1(安全散列算法1)是一種加密散列函數(shù)拉庶,它接受輸入并產(chǎn)生一個(gè)160 位(20 字節(jié))散列值,稱為消息摘要秃励。
  • 2氏仗、MD5:MD5 消息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數(shù)夺鲜,可以產(chǎn)生出一個(gè)128位(16字節(jié))的散列值(hash value)皆尔,用于確保信息傳輸完整一致。
  • 3币励、SHA-2:名稱來自于安全散列算法2(Secure Hash Algorithm 2)的縮寫慷蠕,一種密碼散列函數(shù)算法標(biāo)準(zhǔn),其下又可再分為六個(gè)不同的算法標(biāo)準(zhǔn)食呻,包括了:SHA-224流炕、SHA-256、SHA-384仅胞、SHA-512每辟、SHA-512/224、SHA-512/256干旧。

簽名和校驗(yàn)的主要過程

簽名就是 在摘要的基礎(chǔ)上再進(jìn)行一次加密渠欺,對摘要加密后的數(shù)據(jù)就可以當(dāng)作數(shù)字簽名。
簽名過程:
簽名過程可以細(xì)分為 三步莱革,如下所示:

  • 1峻堰、計(jì)算摘要:通過 Hash 算法提取出原始數(shù)據(jù)的摘要。
  • 2盅视、計(jì)算簽名:再通過基于密鑰(私鑰)的非對稱加密算法對提取出的摘要進(jìn)行加密捐名,加密后的數(shù)據(jù)就是簽名信息。
  • 3闹击、寫入簽名:將簽名信息寫入原始數(shù)據(jù)的簽名區(qū)塊內(nèi)镶蹋。
    校驗(yàn)過程:
    校驗(yàn)過程同樣也可以分為 三步,如下:
  • 1、提取摘要:首先用同樣的 Hash 算法從接收到的數(shù)據(jù)中提取出摘要贺归。
  • 2淆两、解密簽名:使用發(fā)送方的公鑰對數(shù)字簽名進(jìn)行解密,解密出原始摘要拂酣。
  • 3秋冰、比較摘要:如果解密后的數(shù)據(jù)和提取的摘要一致,則校驗(yàn)通過婶熬;如果數(shù)據(jù)被第三方篡改過剑勾,解密后的數(shù)據(jù)和摘要將會(huì)不一致,則校驗(yàn)不通過赵颅。
    那么虽另,我們該如何保證公鑰的可靠性呢?答案是 數(shù)字證書饺谬。

數(shù)字證書

數(shù)字證書是 身份認(rèn)證機(jī)構(gòu)(Certificate Authority)頒發(fā)的捂刺,主要包含了以下 六類信息:

  • 1、證書頒發(fā)機(jī)構(gòu)
  • 2募寨、證書頒發(fā)機(jī)構(gòu)簽名
  • 3族展、證書綁定的服務(wù)器域名
  • 4、證書版本绪商、有效期
  • 5苛谷、簽名使用的加密算法(非對稱算法,如 RSA)
  • 6格郁、公鑰等

接收方收到消息后腹殿,需要先向 CA 驗(yàn)證證書的合法性,再進(jìn)行簽名校驗(yàn)例书。

需要注意的是锣尉,Apk 的證書通常是自簽名的,也就是由開發(fā)者自己制作决采,沒有向 CA 機(jī)構(gòu)申請自沧。Android 在安裝 Apk 時(shí)并沒有校驗(yàn)證書本身的合法性,只是從證書中提取公鑰和加密算法树瞭,這也正是對第三方 Apk 重新簽名后拇厢,還能夠繼續(xù)在沒有安裝這個(gè) Apk 的系統(tǒng)中繼續(xù)安裝的原因

keystore 和證書格式

keystore 文件中包含了 私鑰晒喷、公鑰和數(shù)字證書孝偎。根據(jù)編碼不同,keystore 文件分為很多種凉敲,Android 使用的是 Java 標(biāo)準(zhǔn) keystore 格式 JKS(Java Key Storage)衣盾,所以通過 Android Studio 導(dǎo)出的 keystore 文件是以 .jks 結(jié)尾的寺旺。

keystore 使用的 證書標(biāo)準(zhǔn)是 X.509,X.509 標(biāo)準(zhǔn)也有多種 編碼格式势决,常用的有兩種:pem(Privacy Enhanced Mail)和 der(Distinguished Encoding Rules)阻塑。jks 使用的是 der 格式,但是果复,Android 也支持直接使用 pem 格式的證書進(jìn)行簽名陈莽。

下面,我們了解下兩種證書編碼格式的區(qū)別据悔,如下所示:

  • DER(Distinguished Encoding Rules):二進(jìn)制格式传透,所有類型的證書和私鑰都可以存儲(chǔ)為 der 格式。
  • PEM(Privacy Enhanced Mail):base64 編碼极颓,內(nèi)容以-----BEGIN xxx----- 開頭,以-----END xxx----- 結(jié)尾群嗤。

jarsigner 和 apksigner 的區(qū)別

Android 提供了 兩種對 Apk 的簽名方式菠隆,一種是基于 JAR 的簽名方式,另一種是基于 Apk 的簽名方式狂秘,它們的 主要區(qū)別在于使用的簽名文件不一樣:jarsigner 使用 keystore 文件進(jìn)行簽名骇径;而 apksigner 除了支持使用 keystore 文件進(jìn)行簽名外,還支持直接指定 pem 證書文件和私鑰進(jìn)行簽名者春。

在我們簽名時(shí)破衔,除了要指定 keystore 文件和密碼外,也要指定 alias 和 key 的密碼钱烟,這是為什么呢晰筛?

keystore 是一個(gè)密鑰庫,也就是說它可以存儲(chǔ)多對密鑰和證書拴袭,keystore 的密碼是用于保護(hù) keystore 本身的读第,每一對密鑰和證書是通過 alias 來區(qū)分的。所以 jarsigner 是支持使用多個(gè)證書對 Apk 進(jìn)行簽名的拥刻,apksigner 也同樣支持怜瞒。

Android Apk V1 驗(yàn)證簽名的原理

Android Apk V1 驗(yàn)證簽名的過程主要可以分為如下 四步:

  • 1、解析出 CERT.RSA 文件中的證書般哼、公鑰吴汪,解密 CERT.RSA 中的加密數(shù)據(jù)。
  • 2蒸眠、解密結(jié)果和 CERT.SF 的指紋進(jìn)行對比漾橙,保證 CERT.SF 沒有被篡改。
  • 3黔宛、接著近刘,將 CERT.SF 中的內(nèi)容再和 MANIFEST.MF 中的指紋對比擒贸,保證 MANIFEST.MF 文件沒有被篡改。
  • 4觉渴、MANIFEST.MF 中的內(nèi)容和 APK 所有文件指紋逐一對比介劫,保證 APK 沒有被篡改。

附1案淋、查看 Gradle 源碼

這里提供一種Gradle源碼的查看方式座韵,就是導(dǎo)入Gradle庫,然后在External Libraries中查看:

implementation 'com.android.tools.build:gradle:4.1.1'

先以依賴的方式導(dǎo)入gradle庫踢京,然后編譯誉碴,就能在左側(cè)External Libraries欄中看到源碼了:

參考

深入探索編譯插樁技術(shù)(一、編譯基礎(chǔ))
從構(gòu)建工具看 Android APK 編譯打包流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓣距,一起剝皮案震驚了整個(gè)濱河市黔帕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹈丸,老刑警劉巖成黄,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逻杖,居然都是意外死亡奋岁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門荸百,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闻伶,“玉大人,你說我怎么就攤上這事够话±逗玻” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵更鲁,是天一觀的道長霎箍。 經(jīng)常有香客問我,道長澡为,這世上最難降的妖魔是什么漂坏? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮媒至,結(jié)果婚禮上顶别,老公的妹妹穿的比我還像新娘。我一直安慰自己拒啰,他們只是感情好驯绎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谋旦,像睡著了一般剩失。 火紅的嫁衣襯著肌膚如雪屈尼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天拴孤,我揣著相機(jī)與錄音脾歧,去河邊找鬼。 笑死演熟,一個(gè)胖子當(dāng)著我的面吹牛鞭执,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芒粹,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼兄纺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了化漆?” 一聲冷哼從身側(cè)響起估脆,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎获三,沒想到半個(gè)月后旁蔼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疙教,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伞租。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞谓。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖葵诈,靈堂內(nèi)的尸體忽然破棺而出裸弦,到底是詐尸還是另有隱情,我是刑警寧澤作喘,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布理疙,位于F島的核電站,受9級特大地震影響泞坦,放射性物質(zhì)發(fā)生泄漏窖贤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一贰锁、第九天 我趴在偏房一處隱蔽的房頂上張望赃梧。 院中可真熱鬧,春花似錦豌熄、人聲如沸授嘀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹄皱。三九已至览闰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巷折,已是汗流浹背压鉴。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盔几,地道東北人晴弃。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像逊拍,于是被迫代替她去往敵國和親上鞠。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355