編輯:關於Android編程
在Android 系統中,所有安裝到系統的應用程序都必有一個數字證書,這個數字證書就是這個應用的簽名。此數字證書用於標識應用程序的作者和在應用程序之間建立信任關系,如果一個 permission的protectionLevel為signature,那麼就只有那些跟該permission所在的程序擁有同一個數字證書的應用程序才能取得該權限。這個數字證書並不需要權威的數字證書簽名機構認證,它只是用來讓應用程序包自我認證的。
為了保證每個應用程序開發商合法ID,防止部分開放商可能通過使用相同的Package Name來混淆替換已經安裝的程序,冒充原來的應喲功能。我們需要對我們發布的APK文件進行唯一簽名,保證我們每次發布的版本的一致性(如自動更新不會因為版本不一致而無法安裝)。
概括起來有以下幾點:
(1 ) 發送者的身份認證
(2)保證輸入信息的完整性
(3)防止交易中的抵賴發生
創建key,需要用到 keytool,使用產生的key對apk簽名用到的是jarsigner。
keytool -genkey -alias demo.keystore -keyalg RSA -validity 40000 -keystore demo.keystore
/*說明:-genkey 產生密鑰
-alias demo.keystore 別名 demo.keystore
-keyalg RSA 使用RSA算法對簽名加密
-validity 40000 有效期限4000天
-keystore demo.keystore */
jarsigner -verbose -keystore demo.keystore -signedjar demo_signed.apk demo.apk demo.keystore
說明:-verbose 輸出簽名的詳細信息
-keystore demo.keystore 密鑰庫位置
-signedjar demor_signed.apk demo.apk demo.keystore 正式簽名,三個參數中依次為簽名後產生的文件demo_signed,要簽名的文件demo.apk和密鑰庫demo.keystore.
查看密鑰信息
keytool -printcert -v -file miyao.cer
keytool -list -keystore debug.keystore
java -jar signapk.jar platform.x509.pem platform.pk8 MyDemo.apk MyDemo_signed.apk
signapk的參數分別為公鑰,私鑰,需要簽名的apk,簽名後的apk
使用源碼內置的簽名可以獲取系統的不同權限。
簽名之後,可以用zipalign(壓縮對齊)優化APK文件
zipalign -v 4 demo_signed.apk final.apk
使用andorid源碼打包apk是使用signapk簽名的,我們分析一下這個過程
程序遍歷app包中的所有文件(entry),對非文件夾非簽名文件的文件,逐個生成SHA1的數字簽名信息,再用Base64進行編碼。具體代碼見這個方法:
private static Manifest addDigestsToManifest(JarFile jar)
/** Add the SHA1 of every file to the manifest, creating it if necessary. */ private static Manifest addDigestsToManifest(JarFile jar) throws IOException, GeneralSecurityException { Manifest input = jar.getManifest(); Manifest output = new Manifest(); Attributes main = output.getMainAttributes(); if (input != null) { main.putAll(input.getMainAttributes()); } else { main.putValue("Manifest-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); } MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] buffer = new byte[4096]; int num; // We sort the input entries by name, and add them to the // output manifest in sorted order. We expect that the output // map will be deterministic. TreeMapbyName = new TreeMap (); for (Enumeration e = jar.entries(); e.hasMoreElements(); ) { JarEntry entry = e.nextElement(); byName.put(entry.getName(), entry); } for (JarEntry entry: byName.values()) { String name = entry.getName(); if (!entry.isDirectory() && (stripPattern == null || !stripPattern.matcher(name).matches())) { InputStream data = jar.getInputStream(entry); while ((num = data.read(buffer)) > 0) { md.update(buffer, 0, num); } Attributes attr = null; if (input != null) attr = input.getAttributes(name); attr = attr != null ? new Attributes(attr) : new Attributes(); attr.putValue("SHA1-Digest", new String(Base64.encode(md.digest()), "ASCII")); output.getEntries().put(name, attr); } } return output; }
之後將生成的簽名寫入MANIFEST.MF文件。關鍵代碼如下:
private static void signFile(Manifest manifest, JarFile inputJar, X509Certificate[] publicKey, PrivateKey[] privateKey, JarOutputStream outputJar) throws Exception { // Assume the certificate is valid for at least an hour. long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000; JarEntry je; // Everything else copyFiles(manifest, inputJar, outputJar, timestamp); // MANIFEST.MF je = new JarEntry(JarFile.MANIFEST_NAME); je.setTime(timestamp); outputJar.putNextEntry(je); manifest.write(outputJar); int numKeys = publicKey.length; for (int k = 0; k < numKeys; ++k) { // CERT.SF / CERT#.SF je = new JarEntry(numKeys == 1 ? CERT_SF_NAME : (String.format(CERT_SF_MULTI_NAME, k))); je.setTime(timestamp); outputJar.putNextEntry(je); ByteArrayOutputStream baos = new ByteArrayOutputStream(); writeSignatureFile(manifest, baos); byte[] signedData = baos.toByteArray(); outputJar.write(signedData); // CERT.RSA / CERT#.RSA je = new JarEntry(numKeys == 1 ? CERT_RSA_NAME : (String.format(CERT_RSA_MULTI_NAME, k))); je.setTime(timestamp); outputJar.putNextEntry(je); writeSignatureBlock(new CMSProcessableByteArray(signedData), publicKey[k], privateKey[k], outputJar); } }
SHA1數字簽名。簡 單地說,它就是一種安全哈希算法,類似於MD5算法。它把任意長度的輸入,通過散列算法變成固定長度的輸出(這裡我們稱作“摘要信息”)。你不能僅通過這 個摘要信息復原原來的信息。另外,它保證不同信息的摘要信息彼此不同。因此,如果你改變了apk包中的文件,那麼在apk安裝校驗時,改變後的文件摘要信息與MANIFEST.MF的檢驗信息不同,於是程序就不能成功安裝。
對前一步生成的Manifest,使用SHA1-RSA算法,用私鑰進行簽名。關鍵代碼如下:
private static void writeSignatureFile(Manifest manifest, OutputStream out) throws IOException, GeneralSecurityException { Manifest sf = new Manifest(); Attributes main = sf.getMainAttributes(); main.putValue("Signature-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); MessageDigest md = MessageDigest.getInstance("SHA1"); PrintStream print = new PrintStream( new DigestOutputStream(new ByteArrayOutputStream(), md), true, "UTF-8"); // Digest of the entire manifest manifest.write(print); print.flush(); main.putValue("SHA1-Digest-Manifest", new String(Base64.encode(md.digest()), "ASCII")); Mapentries = manifest.getEntries(); for (Map.Entry entry : entries.entrySet()) { // Digest of the manifest stanza for this entry. print.print("Name: " + entry.getKey() + "\r\n"); for (Map.Entry
RSA是一種非對稱加密算法。用私鑰通過RSA算法對摘要信息進行加密。在安裝時只能使用公鑰才能解密它。解密之後,將它與未加密的摘要信息進行對比,如果相符,則表明內容沒有被異常修改。
生成MANIFEST.MF沒有使用密鑰信息,生成CERT.SF文件使用了私鑰文件。那麼我們可以很容易猜測到,CERT.RSA文件的生成肯定和公鑰相關。
CERT.RSA文件中保存了公鑰、所采用的加密算法等信息。
生成CERT.RSA文件:核心代碼如下:
// CERT.RSA / CERT#.RSA je = new JarEntry(numKeys == 1 ? CERT_RSA_NAME : (String.format(CERT_RSA_MULTI_NAME, k))); je.setTime(timestamp); outputJar.putNextEntry(je); writeSignatureBlock(new CMSProcessableByteArray(signedData), publicKey[k], privateKey[k], outputJar);
分根據APK包的簽名流程,我們可以意識到:
1、 Android簽名機制其實是對APK包完整性和發布機構唯一性的一種校驗機制。
2、 Android簽名機制不能阻止APK包被修改,但修改後的再簽名無法與原先的簽名保持一致。(擁有私鑰的情況除外)。
3、 APK包加密的公鑰就打包在APK包內,且不同的私鑰對應不同的公鑰。換句話言之,不同的私鑰簽名的APK公鑰也必不相同。所以我們可以根據公鑰的對比,來判斷私鑰是否一致。
4、簽名是不對META-INF的文件校驗的,可以利用這個漏洞做一些事情。比如快速動態做渠道標記
1) App升級。 使用相同簽名的升級軟件可以正常覆蓋老版本的軟件,否則系統比較發現新版本的簽名證書和老版本的簽名證書不一致,不會允許新版本安裝成功的。
2) App模塊化。android系統允許多個安裝包的App運行在同一個進程中,如果運行在同一個進程中,則他們相當於同一個App,但是你可以單獨對他們升級更新,這是一種App級別的模塊化思路。
3) 允許代碼和數據共享。android中提供了一個基於簽名的Permission標簽。通過允許的設置,我們可以實現對不同App之間的訪問和共享
在app的AndroidManifest.xml中加上 android:sharedUserId="android.uid.system"然後使用目標系統的platform密鑰來重新給apk文件簽名。這首先找到密鑰文件,在Android源碼目錄中的位置 是“build\target\product\security”,下面的platform.pk8和platform.x509.pem兩個文件。然 後用Android提供的Signapk工具來簽名,signapk的源代碼是在“build\tools\signapk”下,用法為“signapk platform.x509.pem platform.pk8 input.apk output.apk”,文件名最好使用絕對路徑防止找不到,也可以修改源代碼直接使用。這樣修改後的應用就會獲取系統權限。
?安裝apk時,通過CERT.RSA查找公鑰和算法,並對CERT.SF進行解密和簽名驗證,確認MANIFEST.MF,最終對每個文件簽名校驗。
升級時,android也會進行簽名驗證。如果遇到以下情況,都不能完成升級:
1) 兩個應用,名字相同,簽名不同
2) 升級時前一版本簽名,後一版本沒簽名。
3) 升級時前一版本為DEBUG簽名,後一個為自定義簽名。
4) 升級時前一版本為Android源碼中的簽名,後一個為DEBUG簽名或自定義簽名。
5) 安裝未簽名的程序。
6) 安裝升級已過有效期的程序。
在Android開發中;Activity之間傳遞參數是常見的事;如果我們要在Activity之間傳遞圖片;1。MainActivity中包括一個ImageView;當我們
首先來看一下我實現的效果圖: 從”我的關注”開始,每一個條目都是一個自定義組合控件.下面,我來講解一下具體實現步驟: 一.根據
我看到越來越多的應用使用這樣的效果,如QQ空間5.0的主界面,確實很好看!大概就搜了一下相關的實現方式,發現早就有了相關的方案: 仿QQ空間滾動ActionBar透明度變
一直有人問我說,一個項目拿到需求以後怎麼去實現,還有一個app如何去構思,如何去下手,該從那裡去寫,這裡我來簡單的說下,這裡我們拿一個最簡單的天氣預報來說明。宏觀上面來看