概述 Android 包括一個應用程式架構、幾個應用程式庫和一個基於 Dalvik 虛擬機器的運行時,所有這些都運行在 Linux 核心之上。通過利用 Linux 核心的優勢,Android 得到了大量作業系統服務,包括進程和記憶體管理、網路堆棧、驅動程式、硬體抽象層以及與本文主題 —— 安全性 —— 相關的服務。 常用縮寫詞
- ADT:Android 開發工具
- API:API
- IDE:整合式開發環境
- JDK:Java 開發包
- URL:統一資源識別項
- XML:可延伸標記語言 (XML)
前提條件 要跟隨本文,需要具備以下技能和工具:
- 基本瞭解 Java 技術和如何使用 Eclipse(或者您喜歡的 IDE)
- Java Development Kit(需要版本 5 或 6)
- Eclipse(版本 3.4 或 3.5)
- Android SDK 和 ADT 外掛程式
有關下載和設定方面的資訊,請參見本文結尾的 參考資料 部分。 沙箱、進程和許可權 使用者識別碼:Linux 與 Android 在 Linux 中,一個使用者識別碼 識別一個給定使用者;在 Android 上,一個使用者識別碼 識別一個應用程式。應用程式在安裝時被分配使用者識別碼,應用程式在裝置上的存續期間內,使用者識別碼 保持不變。許可權是關於允許或限制應用程式(而不是使用者)訪問裝置資源。 Android 使用沙箱的概念來實現應用程式之間的分離和許可權,以允許或拒絕一個應用程式訪問裝置的資源,比如說檔案和目錄、網路、感應器和 API。為此,Android 使用一些 Linux 工具 + 生產力(比如說進程層級的安全性、與應用程式相關的使用者和組 ID,以及許可權),來實現應用程式被允許執行的操作。 概念上講,沙箱可以表示為 圖 1 所示。 圖 1. 兩個 Android 應用程式,各自在其自己的基本沙箱或進程上 Android 應用程式運行在它們自己的 Linux 進程上,並被分配一個惟一的使用者識別碼。預設情況下,運行在基本沙箱進程中的應用程式沒有被分配許可權,因而防止了此類應用程式訪問系統或資源。但是 Android 應用程式可以通過應用程式的 manifest 檔案請求許可權。 通過做到以下兩點,Android 應用程式可以允許其他應用程式訪問它們的資源:
- 聲明適當的 manifest 許可權
- 與其他信任的應用程式運行在同一進程中,從而共用對其資料和代碼的訪問
後者示範在 圖 2 中。 圖 2. 兩個 Android 應用程式,運行在同一進程上 不同的應用程式可以運行在相同的進程中。對於此方法,首先必須使用相同的私密金鑰簽署這些應用程式,然後必須使用 manifest 檔案給它們分配相同的 Linux 使用者識別碼,這通過用相同的值/名定義 manifest 屬性 android:sharedUserId 來做到。 開發人員用例 圖 3 示範了很多在開發 Android 應用程式時會發現的與安全性相關的用例。 圖 3. 編寫 Android 應用程式時出現的安全領域
- 應用程式或程式碼簽署是這樣一個過程,即產生私人、公用密鑰和公用密鑰認證,簽署和最佳化應用程式。
- 許可權是 Android 平台的一種安全機制,以允許或限制應用程式訪問受限的 API 和資源。預設情況下,Android 應用程式沒有被授予任何許可權,不允許它們訪問裝置上受保護的 API 或資源,從而保證了它們的安全。許可權必須被請求,定義了定製的許可權,檔案和內容提供者就可以受到保護。確保在運行時檢查、執行、授予和撤銷許可權。
接下來,更加詳細地來看一下每個安全領域。 應用程式簽名 所有 Android 應用程式都必須被簽名。應用程式或程式碼簽署是一個這樣的過程,即使用私人密鑰數字地簽署一個給定的應用程式,以便:
- 識別代碼的作者
- 檢測應用程式是否發生了改變
- 在應用程式之間建立信任
基於這一信任關係,應用程式可以安全地共用代碼和資料。 使用相同數位簽章簽署的兩個應用程式可以相互授予許可權來訪問基於簽名的 API,如果它們共用使用者識別碼,那麼也可以運行在同一進程中,從而允許訪問對方的代碼和資料。 應用程式簽名首先是產生一個私人、公用金鑰組和一個相關公用密鑰認證,簡稱為公用密鑰認證。 構建 Android 應用程式時可以採用偵錯模式和發布模式:
- 使用 Android 構建工具(命令列和 Eclipse ADT)構建的應用程式是用一個調試私人密鑰自動簽名的;這些應用程式被稱為偵錯模式應用程式。偵錯模式應用程式用於測試,不能夠發布。注意,未簽名的或者使用調試私人密鑰簽名的應用程式不能夠通過 Android Market 發布。
- 您準備發布自己的應用程式時,必須構建一個發布模式的版本,這意味著用私人密鑰簽署應用程式。
Android 中的程式碼簽署採用一種比其他移動平台中要簡單得多的方式。在 Android 上,認證可以是自簽名的,這就是說,無需認證授權。這種方法簡化了發布過程和相關的成本。 接下來,介紹如何從命令列以及通過使用 Eclipse ADT 手動簽署 Android 應用程式。本文中不介紹第三種方法,即使用 Ant。 手動建立私人、公用密鑰和公用密鑰認證 回想一下,偵錯模式應用程式是使用調試密鑰/認證由構建工具自動簽名的。要簽署一個發布模式的應用程式,首先必鬚生成私人、公用金鑰組和公用密鑰認證。可以手動地或者通過使用 Eclipse ADT 簽署應用程式。兩種方法中都使用了 Java Developer Kit (JDK) keytool 密鑰和認證管理工具 + 生產力。 要手動產生私人、公用密鑰資訊,可以從命令列使用 keytool,如 清單 1 所示。 清單 1. 使用 keytool 產生私人/公用密鑰和認證
keytool -genkey -v -alias <alias_name> -keystore <keystore.name> -keyalg RSA -keysize 2048 -validity <number of days> |
注意:清單 1 假設 JDK 已安裝在您的電腦上,並且 JAVA_HOME 路徑被正確定義為指向您的 JDK 目錄(參見 參考資料,獲得下載和設定資訊)。 在 清單 1 中,-genkey 表示一個公用、私人金鑰組項,以及一個 X.509 v1 自簽署的單個元素憑證鏈結,其中包含產生的公用密鑰。-v 表示冗長模式。-alias 是用於 keystore 項的別名,keystore 儲存產生的私人密鑰和認證。-keystore 表示使用的密鑰倉庫的名稱。-keyalg 是用來產生金鑰組的演算法。-keysize 是產生的密鑰大小,其中預設大小是 1024,但是推薦大小是 2048。-validity 是有效天數;推薦採用大於 1000 的值。 注意:產生密鑰之後,一定要保證密鑰的安全。不要共用私人密鑰,也不要在命令列或指令碼中指定密鑰;注意,keytool 和 jarsigner 會提示輸入密碼。關於這一技巧和其他技巧,請參考 Android Developers 網站的 “Securing Your Private Key”(參見 參考資料 中的連結)。 Keytool 提示您輸入名和姓、公司、城市、州、國家,從這些資訊產生一個 X.500 Distinguished Name(更多資訊請參見 參考資料),還要輸入保護私人密鑰和密鑰倉庫本身的密碼。 對於有效期間,請確保使用超出應用程式本身和相關應用程式預期生命期的時期。如果您是在 Android Market 上發布應用程式,那麼有效期間必須晚於 2033 年 10 月 22 日結束;否則不能上傳。此外,擁有長壽命的認證讓升級應用程式更為容易。幸運的是,Android Market 強制採用長壽命的認證,以協助您避免此類問題。 手動簽署應用程式 接下來,使用 jarsigner 工具(它是 JDK 的一部分)簽署未簽名的應用程式:
jarsigner -verbose -keystore <keystore.name> <my_application.apk> <alias_name> |
在上述代碼中,-verbose 表示冗長模式,-keystore 表示使用的密鑰倉庫的名稱。接下來是應用程式的名稱 (.apk),最後是用於私人密鑰的別名。 Jarsigner 提示您輸入使用密鑰倉庫和私人密鑰時的密碼。 應用程式可以使用不同的密鑰進行多次簽名,用相同私人密鑰簽名的應用程式之間可以建立一種信任關係,並且可以運行在同一進程中,共用代碼和資料。 手動最佳化應用程式 簽署過程的最後一步是最佳化應用程式,以便資料邊界與檔案的開始是記憶體對齊的,這種技術有助於改善運行時效能和記憶體利用率。要簽署應用程式,可以使用 zipalign:
zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk |
在前面的代碼中,-v 表示冗長輸出。數字 4 表示使用四位元組對齊(總是使用四位元組)。下一個參數是輸入已簽署應用程式的檔案名稱 (.apk),它必須用您的私人密鑰簽署。最後一個參數是輸出檔案名;如果覆蓋現有應用程式,則添加一個 -f。 手動驗證應用程式已經簽署 要驗證應用程式已經簽署,可以使用 Jarsigner,這次傳遞 -verify 標誌:
jarsigner -verify -verbose -certs my_application.apk |
在前面的代碼中,-verify 表示驗證應用程式;-verbose 表示冗長模式;-certs 表示展示建立密鑰的 CN 欄位,最後一個參數是要驗證的 Android 應用程式套件的名稱。 注意:如果 CN 讀入 "Android Debug",那麼意味著應用程式是用調試密鑰簽署的,這表明不能發布;如果您計劃在 Android Market 上發布您的應用程式,一定要記得使用私人密鑰。 剛才學習了如何手動建立私人、公用密鑰,以及簽署和最佳化應用程式。接下來,瞭解如何使用 Eclipse ADT 自動建立私人、公用密鑰,以及簽署和最佳化應用程式。 使用 Eclipse ADT 建立密鑰和認證,以及簽署和最佳化應用程式 要使用 Eclipse ADT 產生密鑰,必須匯出應用程式。有兩種方法從 Eclipse 匯出應用程式:
- 匯出您必須手動簽署的應用程式的未簽署 版本
- 匯出應用程式的已簽署 版本,其中所有步驟都由 ADT 為您代勞
匯出未簽署的應用程式 您可以匯出您必須手動簽署的應用程式的未簽署版本。就是說,您需要手動運行 keytool(如前所述,是為了產生密鑰)和 Jarsigner(為了簽署應用程式),並使用 zipalign 工具最佳化應用程式,跟前面解釋的那樣。 要使用 ADT 匯出應用程式的未簽署版本,可以按右鍵項目並選擇 Android Tools>Export Unsigned Application Package(參見 圖 4)。 圖 4. 匯出未簽署的應用程式 選中之後,ADT 提示您選擇將未簽署應用程式匯出到的目錄。記住,一旦應用程式被匯出,您就必須手動簽署和最佳化應用程式,跟前面介紹的那樣。 匯出已簽署的應用程式 利用 Eclipse ADT,您可以匯出應用程式的已簽署版本。使用這種方法,ADT 提示您輸入以下內容:
- 使用現有 KeyStore 或者建立新的受保護 KeyStore 所需的資訊
- 建立受保護私人密鑰所需的資訊
- 產生公用密鑰認證所需的資訊
要匯出已簽署的應用程式,可以按右鍵項目,但是這一次選擇功能表項目 Android Tools->Export Signed Application Package,如 圖 5 所示。 圖 5. 匯出已簽署的應用程式 此時,Export Wizard 執行,如 圖 6 所示。 圖 6. Export Wizard 在 圖 7 中,選擇一個現有的密鑰倉庫(或者建立一個新的)和認證。 圖 7. Export Wizard:密鑰倉庫選擇 在 圖 8 中,輸入資訊以建立私人密鑰和數位憑證。 圖 8. Export Wizard:建立私人密鑰和數位憑證 在 圖 9 中,輸入目標檔案的路徑和名稱,並驗證有效期間。 圖 9. 輸入目標檔案的路徑和名稱 完成時,您就有了一個發布模式的已簽署和已最佳化的應用程式,您發行就緒它。 另外,您也可以使用 Android Manifest 工具調用 Export Wizard,如 圖 10 所示。 圖 10. 使用 Android Manifest 工具調用 Export Wizard 應用程式簽署之後,下一步由您在 manifest 中定義應用程式需要的許可權。接下來將描述這一過程。 注意,Android Developer 網站有非常好的關於應用程式簽署的文檔,當有 Android 平台的新版本可用時,這些文檔都會更新(參見 參考資料,瞭解更多資訊)。 使用許可權 許可權是一種 Android 平台安全機制,旨在允許或限制應用程式訪問受限的 API 和資源。預設情況下,Android 應用程式沒有被授予許可權,這通過不允許它們訪問裝置上的受保護 API 或資源,確保了它們的安全。許可權在安裝期間通過 manifest 檔案由應用程式請求,由使用者授予或不授予。 Android 定義長長的一系列 manifest 許可權,以保護系統或其他應用程式的各個方面。要請求許可權,可以在 manifest 檔案中聲明一個 <user-permission> 屬性:
<uses-permission android:name="string" /> |
其中 android:name 指定許可權的名稱。 要得到所有 Android 定義的 manifest 許可權的列表,請參見 Manifest.permisson 頁面。清單 2 是一個 manifest 檔案的例子,它請求使用 Internet 的許可權和寫到外部儲存空間的許可權: 清單 2. 聲明(請求)許可權
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.cenriqueortiz.tutorials.datastore" android:installLocation="auto"> <application : : : </application> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </manifest> |
應用程式可以定義它們自己的定製許可權,以保護應用程式資源。其他應用程式想要訪問一個應用程式的受保護資源,就必須通過它們自己的 manifest 檔案請求適當的許可權。清單 3 展示了一個如何定義許可權的例子。 清單 3. 聲明定製許可權
<permission xmlns:android="http://schemas.android.com/apk/res/android" android:name="com.cenriqueortiz.android.ACCESS_FRIENDS_LIST" android:description="@string/permission_description" android:label="@string/permission_label" android:protectionLevel="normal" > </permission> |
在 清單 3 中,通過指定最少的屬性,即 name、description、label 和 protectionLevel,定義了一個定製許可權。也可以定義其他屬性,但是這裡沒做介紹。 特別有趣的是 android:protectionLevel 屬性,它表示系統向一個請求許可權的應用程式授予(或不授予)給定的許可權時應該遵循的方法。保護層級有普通 和危險。前者自動授予許可權(儘管使用者在安裝之前總是可以重審),基於簽名授予許可權(就是說,如果請求許可權的應用程式是用同一認證簽署的);後者表示許可權給予私人資料的訪問權,或者具有另一個潛在的負面影響。有關 <permission> manifest 屬性的更多資訊,請參見 <permission> 頁面(參見 參考資料)。 應用程式可以限制對應用程式及其使用的系統組件(比如 Activity、Service、Content Provider 和 Broadcast Receiver)的訪問。通過像 清單 4 中那樣定義 android:permission 屬性,很容易實現這種限制。這種層級的保護讓應用程式允許或限制其他應用程式訪問系統資源。 清單 4. 定義一個活動的許可權
<activity android:name=".FriendsListActivity" android:label="Friends List"> android:permission="com.cenriqueortiz.android.ACCESS_FRIENDS_LIST" <intent-filter> : : </intent-filter> </activity> |
內容提供者和檔案許可權 內容提供者暴露一個公用 URI,用於惟一地識別它們的資料(參見 參考資料)。要保護此內容提供者,當開始時或者從活動返回結果時,調用者可以設定 Intent.FLAG_GRANT_READ_URI_PERMISSION 和 Intent.FLAG_GRANT_WRITE_URI_PERMISSION,以便授予接收活動許可權,以訪問特定的資料 URI。 應用程式檔案預設是受保護的。檔案基於使用者識別碼 受保護,因而只對所有者應用程式是可訪問的(此應用程式具有相同的使用者識別碼)。正如前面介紹的,共用相同使用者識別碼(並使用相同數位憑證簽署)的應用程式運行在相同進程上,因而共用對它們的應用程式的訪問。 應用程式可以允許其他應用程式或進程訪問它們的檔案。這種允許是通過指定適當的 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 操作模式(以便允許對檔案的讀或寫訪問)或 MODE_PRIVATE(以便以私人模式開啟檔案)而做到的。您可以在建立或開啟檔案時利用以下方法指定操作模式:
- getSharedPreferences(filename, operatingMode)
- openFileOutput(filename, operatingMode)
- openOrCreateDatabase(filename, operatingMode, SQLiteDatabase.CursorFactory)
運行時 Permission API Android 提供各種 API 來在運行時檢查、執行、授予和撤銷許可權。這些 API 是 android.content.Context 類的一部分,這個類提供有關應用程式環境的全域資訊。例如,假設您想要優雅地處理許可權,您可以確定您的應用程式是否被授予了訪問 Internet 的許可權(參見 確定 5)。 清單 5. 使用運行時 Permission API 在運行時檢查許可權
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) { // The Application requires permission to access the // Internet"); } else { // OK to access the Internet } |
要瞭解其他在運行時檢查、執行、授予和撤銷許可權的許可權 API,請參考上下文類。 結束語 本文介紹了 Android 平台上的安全性,包括沙箱、應用程式簽名、應用程式許可權,以及檔案和內容提供者許可權。閱讀完這篇介紹性文章之後,您將能夠使用 Eclipse 手動建立數位憑證,請求應用程式許可權,以及允許或不允許應用程式訪問檔案和內容提供者。此外,您還簡要瞭解了許可權運行時 API,這些 API 允許您在運行時檢查、執行、授予和撤銷許可權。 |