Android 安全机制概述
1 Android?安全機(jī)制概述
Android 是一個權(quán)限分離的系統(tǒng) 。 這是利用 Linux 已有的權(quán)限管理機(jī)制,通過為每一個 Application 分配不同的 uid 和 gid , 從而使得不同的 Application 之間的私有數(shù)據(jù)和訪問( native 以及 java 層通過這種 sandbox 機(jī)制,都可以)達(dá)到隔離的目的 。 與此 同時, Android 還 在此基礎(chǔ)上進(jìn)行擴(kuò)展,提供了 permission 機(jī)制,它主要是用來對 Application 可以執(zhí)行的某些具體操作進(jìn)行權(quán)限細(xì)分和訪問控制,同時提供了 per-URI permission 機(jī)制,用來提供對某些特定的數(shù)據(jù)塊進(jìn)行 ad-hoc 方式的訪問。
1.1 uid?、 gid 、 gids
Android 的權(quán)限分離的基礎(chǔ)是建立在 Linux 已有的 uid 、 gid 、 gids 基礎(chǔ)上的 。
UID 。 Android 在 安裝一個應(yīng)用程序,就會為 它 分配一個 uid (參考 PackageManagerService 中的 newUserLP 實(shí)現(xiàn))。其中普通 A ndroid 應(yīng)用程序的 uid 是從 10000 開始分配 (參見 Process.FIRST_APPLICATION_UID ), 10000 以下是系統(tǒng)進(jìn)程的 uid 。
GID 。對 于普通應(yīng)用程序來說, gid 等于 uid 。由于每個應(yīng)用程序的 uid 和 gid 都不相同, 因此不管是 native 層還是 java 層都能夠達(dá)到保護(hù)私有數(shù)據(jù)的作用 。
GIDS 。 gids 是由框架在 Application 安裝過程中生成,與 Application 申請的具體權(quán)限相關(guān)。 如果 Application 申請的相應(yīng)的 permission 被 granted ,而且 中有對應(yīng)的 gid s , 那么 這個 Application 的 gids 中將 包含這個 gid s 。
uid gid gids?的?詳細(xì)?設(shè)置過程:
請參考?Act?i?vityManagerService?中的?startProcessLocked?。在通過?zygote?來啟動一個?process?時,直接將?uid?傳給?給了?gid?。再通過?zygote?來?fork?出新的進(jìn)程(?zygote.java?中的forkAndSpecialize?),最終在?native?層(?dalvik_system_zygote.c?)中的?forkAndSpecializeCommon?中通過?linux?系統(tǒng)調(diào)用來進(jìn)行?gid?和?uid?和?gids?的設(shè)置。
1.2 permission
一個權(quán)限主要包含三個方面的信息:權(quán)限的名稱;屬于的權(quán)限組;保護(hù)級別。一個權(quán)限組是指把權(quán)限按照功能分成的不同的集合。每一個權(quán)限組包含若干具體權(quán)限,例如在 COST_MONEY 組中包含 android.permission.SEND_SMS , android.permission.CALL_PHONE 等和費(fèi)用相關(guān)的權(quán)限。
每個權(quán)限通過 protectionLevel 來標(biāo)識保護(hù)級別: normal , dangerous , signature , signatureorsystem 。不同的保護(hù)級別代表了程序要使用此權(quán)限時的認(rèn)證方式。 normal 的權(quán)限只要申請了就可以使用; dangerous 的權(quán)限在安裝時需要用戶確認(rèn)才可以使用; signature 和 signatureorsystem 的權(quán)限需要使用者的 app 和系統(tǒng)使用同一個數(shù)字證書。
Package 的權(quán)限信息主要 通過在 AndroidManifest.xml 中通過一些標(biāo)簽來指定。如 <permission> 標(biāo)簽, <permission-group> 標(biāo)簽 <permission-tree> 等標(biāo)簽。如果 package 需要申請使用某個權(quán)限,那么需要使用 <use-permission> 標(biāo)簽來指定。
2?Android permission?管理機(jī)制
2.1 Framework permission?機(jī)制
2.1.1 安裝入口
permission 的初始化,是指 permission 的向系統(tǒng)申請,系統(tǒng)進(jìn)行檢測并授權(quán),并建立相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。絕大多數(shù)的情況下 permission 都是從一個 package 中掃描所得,而這發(fā)生在 package 安裝和升級的時候。一般有如下幾種 安裝入口:
n packageInstaller , package 被下載安裝時會觸發(fā)使用。 packageInstaller 會通過 AppSecurityPermissions 來檢查 dangerous 的權(quán)限,并對用戶給出提示。
n pm 命令 。
n adb install 。最終還是 調(diào)用 pm install 來安裝 apk 包。
n 拷貝即安裝。 PackageManagerService 中使用 AppDirObserver 對 /data/app/ 進(jìn)行監(jiān)視 ,如果有拷貝即觸發(fā)安裝。
這些安裝方式 最終都會通過調(diào)用 PackageManagerService 中的函數(shù)來完成程序的安裝。
2.1.2 permission?創(chuàng)建
第一步,從 AndroidManifest.xml 中提取 permission 信息。主要提取如下信息:
2?shared uid
指定與其它?package?共享同一個?uid?。
2?permission
提取?permissions?標(biāo)簽指定屬性。它使用?permissionInfo?來描述一個權(quán)限的基本信息。需要指定?protectedLevel?信息,并指定所屬?group?信息。它將被添加到這個?package?的?permissions這個?list?結(jié)構(gòu)中。
2?permission-tree
提取?permissions-tree?標(biāo)簽屬性。?permissions-tree?也通過?permissionInfo?來描述,并被添加到?package?的?permissions?這個?list?結(jié)構(gòu)中。?permission-tree?只是一個名字空間,用來向其中動態(tài)添加一些所謂?Dynamic?的?permission?,這些?permission?可以動態(tài)修改。這些?permission?名稱要以?permission-tree?的名稱開頭。它本身不是一種權(quán)限,沒有?protectedLevel?和所屬group?。只是保存了所屬的?packge?和權(quán)限名(帶有?package?前綴的)。
2?permission-group
定義?permission?組信息,用?PermissionGroup?表示。本身不代表一個權(quán)限,會添加進(jìn)入?package?的?permissionGroups?這個?list?中。
2?uses-permission
定義了?package?需要申請的權(quán)限名。將權(quán)限名添加到?package?的?requestedPermissions?這個?list?中。
2?adopt-permissions
將該標(biāo)簽指定的?name?存入?package?的?mAdoptPermissions?這個?list?中。?Name?指定了這個?package?需要從?name?指定的?package?進(jìn)行權(quán)限領(lǐng)養(yǎng)。在?system package?進(jìn)行升級時使用。
第二步。獲取 Package 中的證書,驗(yàn)證,并將簽名信息保存在 Package 結(jié)構(gòu)中。
1.?如果該?package?來自?system img?(系統(tǒng)?app?),那么只需要從該?Package?的?AndroidManifest.xml?中獲取簽名信息,而無需驗(yàn)證其完整性。但是如果這個?package?與其它?package?共享一個?uid?,那么這個共享?uid?對應(yīng)的?sharedUser?中保存的簽名與之不一致,那么簽名驗(yàn)證失敗。
2.?如果是普通的?package?,那么需要提取證書和簽名信息,并對文件的完成性進(jìn)行驗(yàn)證。
第三步。如果是普通的 package ,那么清除 package 的 mAdoptPermissions 字段信息(系統(tǒng) package 升級才使用)。
第四步。如果在 AndroidManifest.xml 中指定了 shared user ,那么先查看全局 list 中( mSharedUsers )是否該 uid 對應(yīng)的 SharedUserSetting 數(shù)據(jù)結(jié)構(gòu),若沒有則新分配一個 uid ,創(chuàng)建 SharedUserSetting 并保存到全局全局 list ( mSharedUsers )中。
mUserIds?保存了系統(tǒng)中已經(jīng)分配的?uid?對應(yīng)的?SharedUserSetting?結(jié)構(gòu)。每次分配時總是從第一個開始輪詢,找到第一個空閑的位置?i?,然后加上?FIRST_APPLICATION_UID?即可。
第五步。創(chuàng)建 PackageSettings 數(shù)據(jù)結(jié)構(gòu)。并將 PackageSettings 與 SharedUserSetting 進(jìn)行綁定。其中 PackageSettings 保存了 SharedUserSetting 結(jié)構(gòu);而 SharedUserSetting 中會使用 PackageSettings 中的簽名信息填充自己內(nèi)部的簽名信息,并將 PackageSettings 添加到一個隊列中,表示 PackageSettings 為其中的共享者之一。
在創(chuàng)建時,首先會以?packageName?去全局?jǐn)?shù)據(jù)結(jié)構(gòu)?mPackages?中查詢是否已經(jīng)有對應(yīng)的?PackageSettings?數(shù)據(jù)結(jié)構(gòu)存在。如果已經(jīng)存在?PackageSettings?數(shù)據(jù)結(jié)構(gòu)(比如這個?package?已經(jīng)被?uninstall?,但是還沒有刪除數(shù)據(jù),此時?package?結(jié)構(gòu)已經(jīng)被釋放)。那么比較該?package?中的簽名信息(從?AndroidManifest?中掃描得到)與?PackageSettings?中的簽名信息是否匹配。如果不匹配但是為?system package?,那么信任此?package?,并將?package?中的簽名信息更新到已有的?PackageSettings?中去,同時如果這個?package?與其它?package?共享了?uid?,而且?shared uid?中保存的簽名信息與當(dāng)前?package?不符,那么簽名也驗(yàn)證失敗。
第六步。如果 mAdoptPermissions 字段不為空,那么處理 permission 的領(lǐng)養(yǎng)(從指定的 package 對應(yīng)的 PackageSettings 中,將權(quán)限的擁有者修改為當(dāng)前 package ,一般在 system app 升級的時候才發(fā)生,在此之前需要驗(yàn)證當(dāng)被領(lǐng)養(yǎng)的 package 已經(jīng)被卸載,即檢查 package 數(shù)據(jù)結(jié)構(gòu)是否存在)。
第七步。添加自定義權(quán)限。將 package 中定義的 permissionGroup 添加到全局的列表 mPermissionGroups 中去;將 package 中定義的 permissions 添加到全局的列表中去(如果是 permission-tree 類型,那么添加到 mSettings.mPermissionTrees ,如果是一般的 permission 添加到 mSettings.mPermissions 中)。
第八步。清除不一致的 permission 信息。
1.?清除不一致的?permission-tree?信息。如果該?permission-tree?的?packageSettings?字段為空,說明還未對該?package?進(jìn)行過解析(若代碼執(zhí)行到此處時?packageSettings?肯定已經(jīng)被創(chuàng)建過),將其?remove?掉。如果?packageSettings?不為空,但是對應(yīng)的?package?數(shù)據(jù)結(jié)構(gòu)為空(說明該?package?已經(jīng)被卸載,但數(shù)據(jù)還有保留),或者?package?數(shù)據(jù)結(jié)構(gòu)中根本不含有這個permission-tree?,那么將這個?permission-tree?清除。
2.?清除不一致的?permission?信息。如果?packageSettings?或者?package?結(jié)構(gòu)為空(未解析該?package?或者被卸載,但數(shù)據(jù)有保留),或者?package?中根本沒有定義該?permission?,那么將該?permission?清除。
第九步。對每一個 package 進(jìn)行輪詢,并進(jìn)行 permission 授權(quán)。
1.?對申請的權(quán)限進(jìn)行檢查,并更新?grantedPermissions?列表
2.?如果其沒有設(shè)置?shared user id?,那么將其?gids?初始化為?mGlobalGids?,它從?permission.xml?中讀取。
3.?遍歷所有申請的權(quán)限,進(jìn)行如下檢查
1?)如果是該權(quán)限是?normal?或者?dangerous?的。通過檢查。
2?)如果權(quán)限需要簽名驗(yàn)證。如果簽名驗(yàn)證通過。還需要進(jìn)行如下檢查
*?如果程序升級,而且是?system package?。那么是否授予該權(quán)限要看原來的?package?是否被授予了該權(quán)限。如果被授予了,那么通過檢查,否則不通過。
*?如果是新安裝的。那么檢查通過。
4.?如果?3?中檢查通過,那么將這個?permission?添加到?package?的?grantedPermissions?列表中,表示這個?permission?申請成功(?granted?)。申請成功的同時會將這個申請到的?permission的?gids?添加到這個?package?的?gids?中去。
5.?將?permissionsFixed?字段標(biāo)準(zhǔn)為?ture?,表示這個?packge?的?permission?進(jìn)行過修正。后續(xù)將禁止對非?system?的?app?的權(quán)限進(jìn)行再次修正。
2.1.3 Dynamic permission?的管理
PackageManagerService 提供了 addPermission/ removePermission 接口用來動態(tài)添加和刪除一些權(quán)限。但是這些權(quán)限必須是所謂的動態(tài)權(quán)限( BasePermission.TYPE_DYNAMIC )。
一個 Package 如果要添加 Dynamic permissions ,首先必須要在 manifest 中申明 <permission-tree> 標(biāo)簽,它實(shí)際上是一個權(quán)限的名字空間(例如,“ com.foo.far ”這個權(quán)限就是 permission-tree “com.foo ”的成員),本身不是一個權(quán)限。一個 Package 只能為自己的 permission-tree 或者擁有相同的 uid 的 package 添加或者刪除權(quán)限。
Package 不能夠通過這種接口去修改在 manifest 中靜態(tài)申請的權(quán)限,否則拋出異常。
首先查找這個?permission?在全局?permission?列表?mSettings.mPermissions?中是否存在。如果存在,而且類型為?BasePermission.TYPE_DYNAMIC?那么根據(jù)傳入的權(quán)限信息修改全局表中的權(quán)限信息,并觸發(fā)?permissions.xml?的持久化。
如果在全局的?permission?列表?mSettings.mPermissions?中沒有找到,先找到這個?permission?所在?permissionTree?,然后添加到全局?permission?列表?mSettings.mPermissions?中去,并觸發(fā)?permissions.xml?的持久化。
2.1.4 Uri permission?的管理
下面兩個 接口 主要用于 Uri permission 的管理 (其實(shí)現(xiàn)在 ActivityManagerService 中)。
//?為指定的?uid?和?targetPkg?添加對某個?content Uri?的讀或者寫權(quán)限。
public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri, int mode) throws RemoteException;
//?清除所有通過?grantUriPermission?對某個?Uri?授予的權(quán)限。
public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode) throws RemoteException;
grantUriPermission 主要的實(shí)現(xiàn)過程分析。
grantUriPermission?分析:
1.?驗(yàn)證?caller?的?ProcessRecord?和?targetPkg?不為空。否則檢測不通過。
2.?驗(yàn)證所請求的?mode?為?Intent.FLAG_GRANT_READ_URI_PERMISSION?或者為?Intent.FLAG_GRANT_WRITE_URI_PERMISSION?,否則不通過。
3.?確保參數(shù)?Uri?是一個?content Uri?。否則,則檢測不通過。
4.?通過?Uri?得到目標(biāo)?ContentProvider?,如果不存在,則檢測不通過。
5.?從?PackageManagerService?中獲得?targetPkg?對應(yīng)的?uid?。
6.?檢查?target uid?所對應(yīng)的?package?是否真正需要這個權(quán)限?
先判斷要申請的是讀還是寫權(quán)限,然后查看對應(yīng)的?ContentProvider?中對應(yīng)的?readPermission writePermission?字段是否保存了權(quán)限名稱。?如果該字段不為空,則以?target uid?和該權(quán)限名去PackageManagerService?中去查找該?uid?是否被?granted?了該權(quán)限。如果已經(jīng)獲得了該權(quán)限,那么無需再去為這個?Activity?去申請這個?Uri?權(quán)限了,返回。否者繼續(xù)執(zhí)行如下操作。
7.?檢查這個?ContentProvider?的?grantUriPermissions?開關(guān)變量,是否允許對其它?package?進(jìn)行權(quán)限的?grant?操作。如果禁止,那么拋出異常。
8.?檢查這個?ContentProvider?是否設(shè)置了?Uri?的過濾類型?uriPermissionPatterns?,如果設(shè)置了過濾類型,則將需要申請權(quán)限的?Uri?與之匹配。匹配不同過,則拋出異常。
9.?檢查調(diào)用者自己是否有權(quán)限訪問這個?Uri?。如果沒有,拋出異常。
10.?從?mGrantedUriPermissions?中取得?target uid?對應(yīng)的?HashMap<Uri, UriPermission>?數(shù)據(jù)結(jié)構(gòu)。用?target uid?和?Uri?生成?UriPermission?并保存在?mGrantedUriPermissions?中。
revokeUriPermission 實(shí)現(xiàn)分析。
找到該?Uri?對應(yīng)的?ContentProvider?,然后刪除?mGrantedUriPermissions?中與?Uri?對應(yīng)的所有權(quán)限。
2.2 permission?的動態(tài)檢查
這里的動態(tài)檢查是指是 package 在程序運(yùn)行過程中進(jìn)行某些操作或者數(shù)據(jù)訪問時才進(jìn)行的 check ,與之對應(yīng)的是應(yīng)用程序安裝或者升級時 PackageManagerService 通過掃描包中的靜態(tài)權(quán)限信息相對應(yīng)。
系統(tǒng)與權(quán)限 檢查 相關(guān)的機(jī)制的實(shí)現(xiàn)主要集中在 PackageManagerService 和 ActivityManagerService 中。 ActivityManagerService 主要負(fù)責(zé)的是底層的 uid 層次的身份檢查; PackageManagerService 則維護(hù)了 uid 到自己擁有的和被授予的權(quán)限的一張表。在通過 ActivityManagerService 的身份檢查后, PackageManagerService 根據(jù)請求者的 uid 來查看這張表,判斷其是否具有相應(yīng)的權(quán)限。
除此之外, per-URI permission 機(jī)制的實(shí)現(xiàn)也需要一張表,它維護(hù)在 ActivityManagerService 中,它建立了從 content URI 到被授權(quán)訪問這個 URI 的 component 之間的映射。但是它也需要借助 PackageManagerService 的機(jī)制來輔助實(shí)現(xiàn)。
2.2.1 framework?提供的接口
Android framework 中提供了一些接口用來對外來的訪問(包括自己)進(jìn)行權(quán)限檢查 。 這些接口 主要通過 ContextWrapper 提供,具體實(shí)現(xiàn)在 ContextImpl 中 。如果 package 接受到外來訪問者的操作請求,那么可以調(diào)用這些接口進(jìn)行權(quán)限檢查。一般情況下可以把這些接口的檢查接口分為兩種,一種是返回錯誤,另一種是拋出異常。
主要包含如下幾組:
n permission 和 uid 檢查 API
下面這一組接口主要用來檢查某個調(diào)用(或者是其它 package 或者是自己)是否擁有訪問某個 permission 的權(quán)限。參數(shù)中 pid 和 uid 可以指定,如果沒有指定,那么 framework 會通過 Binder 來獲取調(diào)用者的 uid 和 pid 信息,加以填充。返回值為 PackageManager.PERMISSION_GRANTED 或者 PackageManager.PERMISSION_DENIED 。
public int checkPermission(String permission, int pid, int uid)?//?檢查某個?uid?和?pid?是否有?permission?權(quán)限
public int checkCallingPermission(String permission)?//?檢查調(diào)用者是否有?permission?權(quán)限,如果調(diào)用者是自己那么返回?PackageManager.PERMISSION_DENIED
public int checkCallingOrSelfPermission(String permission)?//?檢查自己或者其它調(diào)用者是否有?permission?權(quán)限
下面這一組和上面類似,如果遇到檢查不通過時,會拋出異常,打印消息 。
public void enforcePermission(String permission, int pid, int uid, String message)
public void enforceCallingPermission(String permission, String message)
public void enforceCallingOrSelfPermission(String permission, String message)
n per-URI 檢查 API
為某個 package 添加訪問 content Uri 的讀或者寫權(quán)限。
public void grantUriPermission(String toPackage, Uri uri, int modeFlags)
public void revokeUriPermission(Uri uri, int modeFlags)
檢查某個 pid 和 uid 的 package 是否擁有 uri 的讀寫權(quán)限,返回值表示是否被 granted 。
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags)
public int checkCallingUriPermission(Uri uri, int modeFlags)
public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags)
public int checkUriPermission(Uri uri, String readPermission,String writePermission, int pid, int uid, int modeFlags)
檢查某個 pid 和 uid 的 package 是否擁有 uri 的讀寫權(quán)限,如果失敗則拋出異常,打印消息 。
public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message)
public void enforceCallingUriPermission(Uri uri, int modeFlags, String message)
public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message)
public void enforceUriPermission(Uri uri, String readPermission, String writePermission,int pid, int uid, int modeFlags, String message)
2.2.2 實(shí)現(xiàn)分析
ContextImpl.java 中提供的 API ,其實(shí)都是由 ActivityManagerService 中的如下幾個接口進(jìn)行的封裝。
public int checkPermission(String permission, int pid, int uid)?throws RemoteException;?//?主要用于一般的?permission?檢查
public int checkUriPermission(Uri uri, int pid, int uid, int mode) throws RemoteException; //?主要用于?Content Uri?的?permission?檢查
n checkPermission 的實(shí)現(xiàn)分析
1.?如果傳入的?permission?名稱為?null?,那么返回?PackageManager.PERMISSION_DENIED?。
2.?判斷調(diào)用者?uid?是否符合要求?。
1?)?如果?uid?為?0?,說明是?root?權(quán)限的進(jìn)程,對權(quán)限不作控制。
2?)?如果?uid?為?system server?進(jìn)程的?uid?,說明是?system server?,對權(quán)限不作控制。
3?)?如果是?ActivityManager?進(jìn)程本身,對權(quán)限不作控制。
4?)如果調(diào)用者?uid?與參數(shù)傳入的?req uid?不一致,那么返回?PackageManager.PERMISSION_DENIED?。
3.?如果通過?2?的檢查后,再?調(diào)用?PackageManagerService.checkUidPermission?,判斷?這個?uid?是否擁有相應(yīng)的權(quán)限,分析如下?。
1?)?首先它通過調(diào)用?getUserIdLP?,去?PackageManagerService.Setting.mUserIds?數(shù)組中,根據(jù)?uid?查找?uid?(也就是?package?)的權(quán)限列表。一旦找到,就表示有相應(yīng)的權(quán)限。
2?)?如果沒有找到,那么再去?PackageManagerService.mSystemPermissions?中找。這些信息是啟動時,從?/system/etc/permissions/platform.xml?中讀取的。這里記錄了一些系統(tǒng)級的應(yīng)用的?uid?對應(yīng)的?permission?。
3?)返回結(jié)果?。
n 同樣 checkUriPermission 的實(shí)現(xiàn) 主要在 ActivityManagerService 中,分析如下:
1.?如果?uid?為?0?,說明是?root?用戶,那么不控制權(quán)限。
2.?否則,在?ActivityManagerService?維護(hù)的?mGrantedUriPermissions?這個表中查找這個?uid?是否含有這個權(quán)限,如果有再檢查其請求的是讀還是寫權(quán)限。
3 Android?簽名機(jī)制
關(guān)于簽名機(jī)制,其實(shí)分兩個階段。
包掃描階段需要進(jìn)行完整性和證書的驗(yàn)證。普通 package 的簽名和證書是必須要先經(jīng)過驗(yàn)證的。具體做法是對 manifest 下面的幾個文件進(jìn)行完整性檢查。完整性檢查包括這個 jar 包中的所有文件。如果是系統(tǒng) package 的話,只需要使用 AndroidMenifest.xml 這個文件去提取簽名和驗(yàn)證信息就可以了。
在權(quán)限創(chuàng)建階段。如果該 package 來自 system img (系統(tǒng) app ),那么 trust it ,而且使用新的簽名信息去替換就的信息。前提是如果這個 package 與其它 package 共享一個 uid ,那么這個共享 uid 對應(yīng)的 sharedUser 中保存的簽名與之不一致,那么簽名驗(yàn)證失敗。有些時候系卸載一個 app ,但是不刪除數(shù)據(jù),那么其 PackageSettings 信息會保留,其中會保存簽名信息。這樣再安裝是就會出現(xiàn)不一致。
3.1 Android Package?簽名原理
android 中系統(tǒng)和 app 都是需要簽名的。可以自己通過 development/tools/make_key 來生成公鑰和私鑰。
android 源代碼中提供了工具 ./out/host/linux-x86/framework/signapk.jar 來進(jìn)行手動簽名。簽名的主要作用在于限制對于程序的修改僅限于同一來源。系統(tǒng)中主要有兩個地方會檢查。如果是程序升級的安裝,則要檢查新舊程序的簽名證書是否一致,如果不一致則會安裝失敗;對于申請權(quán)限的 protectedlevel 為 signature 或者 signatureorsystem 的,會檢查權(quán)限申請者和權(quán)限聲明者的證書是否是一致的。簽名相關(guān)文件可以從 apk 包中的 META-INF 目錄下找到。
signapk.jar 的源代碼在 build/tools/signapk ,簽名主要有以下幾步:
l 將除去 CERT.RSA , CERT.SF , MANIFEST.MF 的所有文件生成 SHA1 簽名
首先將除了?CERT.RSA?,?CERT.SF?,?MANIFEST.MF?之外的所有非目錄文件分別用?SHA-1?計算摘要信息,然后使用?base64?進(jìn)行編碼,存入?MANIFEST.MF?中。?如果?MANIFEST.MF?不存在,則需要創(chuàng)建。存放格式是?entry name?以及對應(yīng)的摘要
l 根據(jù) 之前計算的 SHA1 摘要信息,以及 私鑰生成 一系列的 signature 并寫入 CERT.SF
對?整個?MANIFEST.MF?進(jìn)行?SHA1?計算,并將摘要信息存入?CERT.SF?中?。然后對之前計算的所有摘要信息使用?SHA1?再次計算數(shù)字簽名,并寫入?CERT.SF?中。
l 把公鑰和簽名信息寫入 CERT.RST
把之前整個的簽名輸出文件?使用私有密鑰計算簽名。同時將簽名結(jié)果,以及之前聲稱的公鑰信息寫入?CERT.RSA?中保存。
3.2 Package?的簽名驗(yàn)證
安裝時對一個 package 的簽名驗(yàn)證的主要邏輯在 JarVerifier.java 文件的 verifyCertificate 函數(shù)中實(shí)現(xiàn)。 其主要的思路是通過提取 cert.rsa 中的證書和簽名信息,獲取簽名算法等信息,然后按照之前對 apk 簽名的方法進(jìn)行計算,比較得到的簽名和摘要信息與 apk 中保存的匹配。
第一步。提取證書信息,并對 cert.sf 進(jìn)行完整性驗(yàn)證。
1.?先找到是否有?DSA?和?RSA?文件?,如果找到則對其進(jìn)行?decode?,然后讀取其中的所有的證書列表(這些證書會被保存在?Package?信息中,供后續(xù)使用)。
2.?讀取這個文件中的簽名數(shù)據(jù)信息塊列表,只取第一個簽名數(shù)據(jù)塊。讀取其中的發(fā)布者和證書序列號。
3.?根據(jù)證書序列號,去匹配之前得到的所有證書,找到與之匹配的證書。
4.?從之前得到的簽名數(shù)據(jù)塊中讀取簽名算法和編碼方式等信息
5.?讀取?cert.sf?文件,并計算整個的簽名,與數(shù)據(jù)塊中的簽名(編碼格式的)進(jìn)行比較,如果相同則完整性校驗(yàn)成功。
第二步。使用 cert.sf 中的摘要信息,驗(yàn)證 MANIFEST.MF 的完整性。
在?cert.sf?中提取?SHA1-Digest-Manifest?或者?SHA1-Digest?開頭的簽名?數(shù)據(jù)塊?(?-Digest-Manifest?這個是整個?MANIFEST.MF?的摘要?信息,其它的是?jar?包中其它文件的摘要信息?),?并逐個對這些數(shù)據(jù)塊?進(jìn)行驗(yàn)證。驗(yàn)證的方法是,現(xiàn)將?cert.sf?看做是很多的?entries?,每個?entries?包含了一些基本信息,如這個?entry?中使用的摘要算法(?SHA1?等),對?jar?包中的哪個文件計算了摘要,摘要結(jié)果是什么。?處理時先找到每個摘要數(shù)據(jù)開中的文件信息,然后從?jar?包中讀取,然后使用?-Digest?之前的摘要算法進(jìn)行計算,如果計算結(jié)果與摘要數(shù)據(jù)塊中保存的信息的相匹配,那么就完成驗(yàn)證。
轉(zhuǎn)載于:https://www.cnblogs.com/aaa2832/archive/2012/12/28/3594761.html
總結(jié)
以上是生活随笔為你收集整理的Android 安全机制概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用ICSharpCode.TextEd
- 下一篇: 关于应用程序配置文件类的使用 总结