androidsign
⑴ android api 簽名是什麼意思
首先我們得知道什麼是摘要,摘要是指採用單向Hash函數對數據進行計算生成的固定長度的Hash值,摘要演算法有Md5,Sha1等,Md5生成的Hash值是128位的數字,即16個位元組,用十六進製表示是32個字元,Sha1生成的Hash值是160位的數字,即20個位元組,用十六進製表示是40個字元。我們是不能通過摘要推算出用於計算摘要的數據,如果修改了數據,那麼它的摘要一定會變化(其實這句話並不正確,只是很難正好找到不同的數據,而他們的摘要值正好相等)。摘要經常用於驗證數據的完整性,很多下載網站都會列出下載文件的md5值或者sha1值。
摘要和簽名沒有任何關系,網上常常將摘要和簽名混為一談,這是錯誤的。簽名和數字簽名是同一個概念,是指信息的發送者用自己的私鑰對消息摘要加密產生一個字元串,加密演算法確保別人無法偽造生成這段字元串,這段數字串也是對信息的發送者發送信息真實性的一個有效證明。其他發送者用他們的私鑰對同一個消息摘要加密會得到不同的簽名,接收者只有使用發送者簽名時使用的私鑰對應的公鑰解密簽名數據才能得到消息摘要,否則得到的不是正確的消息摘要。
數字簽名是非對稱密鑰加密技術+數字摘要技術的結合。
數字簽名技術是將信息摘要用發送者的私鑰加密,和原文以及公鑰一起傳送給接收者。接收者只有用發送者的公鑰才能解密被加密的信息摘要,然後接收者用相同的Hash函數對收到的原文產生一個信息摘要,與解密的信息摘要做比對。如果相同,則說明收到的信息是完整的,在傳輸過程中沒有被修改;不同則說明信息被修改過,因此數字簽名能保證信息的完整性。並且由於只有發送者才有加密摘要的私鑰,所以我們可以確定信息一定是發送者發送的。
另外還需要理解一個概念:數字證書。數字證書是一個經證書授權中心數字簽名的包含公鑰及其擁有者信息的文件。數字證書的格式普遍採用的是X.509V3國際標准,一個標準的X.509數字證書包含以下一些內容:證書的版本信息:
1)證書的序列號,每個證書都有一個唯一的證書序列號;
2)證書所使用的簽名演算法;
3)證書的發行機構名稱,命名規則一般採用X.500格式;
4)證書的有效期,通用的證書一般採用UTC時間格式,它的計時范圍為1950-2049;
5)證書所有人的名稱,命名規則一般採用X.500格式;
6)證書所有人的公開密鑰;
7)證書發行者對證書的簽名。
CERT.RSA包含了數字簽名以及開發者的數字證書。CERT.RSA里的數字簽名是指對CERT.SF的摘要採用私鑰加密後的數據,Android系統安裝apk時會對CERT.SF計算摘要,然後使用CERT.RSA里的公鑰對CERT.RSA里的數字簽名解密得到一個摘要,比較這兩個摘要便可知道該apk是否有正確的簽名,也就說如果其他人修改了apk並沒有重新簽名是會被檢查出來的。
需注意Android平台的證書是自簽名的,也就說不需要權威機構簽發,數字證書的發行機構和所有人是相同的,都是開發者自己,開發者生成公私鑰對後不需要提交到權威機構進行校驗。
⑵ android 系統簽名
也有提到怎麼單獨給一個apk簽名,這里補充一下android的簽名許可權控制機制。
android的標准簽名key有:
testkey
media
latform
hared
以上的四種,可以在源碼的/build/target/proct/security裡面看到對應的密鑰,其中shared.pk8代表私鑰,shared.x509.pem公鑰,一定是成對出現的。
其中testkey是作為android編譯的時候默認的簽名key,如果系統中的apk的android.mk中沒有設置LOCAL_CERTIFICATE的值,就默認使用testkey。
而如果設置成:
LOCAL_CERTIFICATE := platform
就代表使用platform來簽名,這樣的話這個apk就擁有了和system相同的簽名,因為系統級別的簽名也是使用的platform來簽名,此時使用android:sharedUserId="android.uid.system"才有用!
在/build/target/proct/security目錄下有個README,裡面有說怎麼製作這些key以及使用問題(android4.2):
從上面可以看出來在源碼下的/development/tools目錄下有個make_key的腳本,通過傳入兩個參數就可以生成一對簽名用的key。
其中第一個為key的名字,一般都默認成android本身有的,因為很多地方都默認使用了這些名字,我們自定義的話只需要對第二個參數動手腳,定義如下:
C ---> Country Name (2 letter code) ST ---> State or Province Name (full name) L ---> Locality Name (eg, city) O ---> Organization Name (eg, company) OU ---> Organizational Unit Name (eg, section) CN ---> Common Name (eg, your name or your server』s hostname) emailAddress ---> Contact email addre
另外在使用上面的make_key腳本生成key的過程中會提示輸入password,我的處理是不輸入,直接enter,不要密碼!後面解釋,用自定義的key替換/security下面的。
可以看到android源碼裡面的key使用的第二個參數就是上面README裡面的,是公開的,所以要release版本的系統的話,肯定要有自己的簽名key才能起到一個安全控製作用。
在上面提到如果apk中的編譯選項LOCAL_CERTIFICATE沒有設置的話,就會使用默認的testkey作為簽名key,我們可以修改成自己想要的key,按照上面的步驟製作一個releasekey,修改android配置在/build/core/config.mk中定義變數:
在主makefile文件裡面:
ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/target/proct/security/releasekey)
BUILD_VERSION_TAGS += release-key
這樣的話默認的所有簽名將會使用releasekey。
修改完之後就要編譯了,如果上面的這些key在製作的時候輸入了password就會出現如下錯誤:
我在網上找到了合理的解釋:
其實會出現這個錯誤的最根本的原因是多線程的問題。在編譯的時候為了加速一般都會執行make -jxxx,這樣本來需要手動輸入密碼的時候,由於其它線程的運行,就會導致影響當前的輸入終端,所以就會導緻密碼無法輸入的情況!
再編譯完成之後也可以在build.prop中查看到變數:
這樣處理了之後編譯出來的都是簽名過的了,系統才算是release版本
我發現我這樣處理之後,整個系統的算是全部按照我的要求簽名了。
網上看到還有另外的簽名release辦法,但是應該是針對另外的版本的,借用學習一下:
make -j4 PRODUCT-proct_mol-user dist
這個怎麼跟平時的編譯不一樣,後面多了兩個參數PRODUCT-proct_mol-user 和 dist. 編譯完成之後回在源碼/out/dist/目錄內生成個proct_mol-target_files開頭的zip文件.這就是我們需要進行簽名的文件系統.
我的proct_mol 是full_gotechcn,後面加「-user」代表的是最終用戶版本,關於這個命名以及proct_mol等可參考http://blog.csdn.net/jscese/article/details/23931159
編譯出需要簽名的zip壓縮包之後,就是利用/security下面的准備的key進行簽名了:
./build/tools/releasetools/sign_target_files_apks -d /build/target/proct/security out/dist/full_gotechcn-target_files.zip out/dist/signed_target_files.zi
簽名目標文件 輸出成signed_target_files.zi
如果出現某些apk出錯,可以通過在full_gotechcn-target_files.zip前面加參數"-e =" 來過濾這些apk.
然後再通過image的腳本生成imag的zip文件,這種方式不適用與我目前的工程源碼,沒有做過多驗證!
Android簽名機制可劃分為兩部分:(1)ROM簽名機制;(2)第三方APK簽名機制。
Android APK實際上是一個jar包,而jar包又是一個zip包。APK包的簽名實際上使用的是jar包的簽名機制:在zip中添加一個META的子目錄,其中存放簽名信息;而簽名方法是為zip包中的每個文件計算其HASH值,得到簽名文件(*.sf),然後對簽名文件(.sf)進行簽名並把簽名保存在簽名塊文件(*.dsa)中。
在編譯Android源碼生成ROM的過程中,會使用build/target/proct/security目錄中的4個key(media, platform, shared, testkey)來對apk進行簽名。其中,*.pk8是二進制形式(DER)的私鑰,*.x509.pem是對應的X509公鑰證書(BASE64編碼)。build/target/proct/security目錄中的這幾個默認key是沒有密碼保護的,只能用於debug版本的ROM。
要生成Release版本的ROM,可先生成TargetFiles,再使用帶密碼的key對TargetFiles重新簽名,最後由重簽名的TargetFiles來生成最終的ROM。
可以使用Android源碼樹中自帶的工具「development/tools/make_key」來生成帶密碼的RSA公私鑰對(實際上是通過openssl來生成的): $ development/tools/make_key media 『/C=CN/ST=Sichuan/L=Cheng/O=MyOrg/OU=MyDepartment/CN=MyName』 上面的命令將生成一個二進制形式(DER)的私鑰文件「media.pk8」和一個對應的X509公鑰證書文件「media.x509.pem」。其中,/C表示「Country Code」,/ST表示「State or Province」,/L表示「City or Locality」,/O表示「Organization」,/OU表示「Organizational Unit」,/CN表示「Name」。前面的命令生成的RSA公鑰的e值為3,可以修改development/tools/make_key腳本來使用F4 (0×10001)作為e值(openssl genrsa的-3參數改為-f4)。
也可以使用JDK中的keytool來生成公私鑰對,第三方APK簽名一般都是通過keytool來生成公私鑰對的。
可以使用openssl x509命令來查看公鑰證書的詳細信息: $ openssl x509 -in media.x509.pem -text -noout or, $ openssl x509 -in media.x509.pem -inform PEM -text -noout
還可以使用JDK中的keytool來查看公鑰證書內容,但其輸出內容沒有openssl x509全面: $ keytool -printcert -v -file media.x509.pem
有了key之後,可以使用工具「build/tools/releasetools/sign_target_files」來對TargetFiles重新簽名: $ build/tools/releasetools/sign_target_files_apks -d new_keys_dir -o target_files.zip target_files_resigned.zip 其中,new_keys_dir目錄中需要有四個key(media, platform, shared, releasekey)。注意:這里的releasekey將代替默認的testkey(請參考build/tools/releasetools/sign_target_files腳本實現),也就是說,如果某個apk的Android.mk文件中的LOCAL_CERTIFICATE為testkey,那麼在生成TargetFiles時是使用的build/target/proct/security/testkey來簽名的,這里重新簽名時將使用new_keys_dir/releasekey來簽名。
uild/tools/releasetools/sign_target_files_apks是通過host/linux-x86/framework/signapk.jar來完成簽名的。也可以直接使用host/linux-x86/framework/signapk.jar來對某個apk進行簽名: $ java -jar signapk [-w] publickey.x509[.pem] privatekey.pk8 input.jar output.jar 其中,」-w」表示還對整個apk包(zip包)進行簽名,並把簽名放在zip包的comment中。
對於第三方應用開發者而言,對APK簽名相對要簡單得多。第三方應用開發一般採用JDK中的keytool和jarsigner來完成簽名密鑰的管理和APK的簽名。
使用keytool來生成存儲有公私鑰對的keystore: $ keytool -genkey -v -keystore my-release-key.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000
查看生成的密鑰信息: $ keytool -list -keystore my-release-key.keystore -alias mykey -v or, $ keytool -list -keystore my-release-key.keystore -alias mykey -rfc (註:獲取Base64格式的公鑰證書,RFC 1421)
導出公鑰證書: $ keytool -export -keystore mystore -alias mykey -file my.der (註:二進制格式公鑰證書,DER) $ keytool -export -keystore mystore -alias mykey -file my.pem -rfc (註:Base64格式公鑰證書,PEM)
對APK進行簽名: $ jarsigner -verbose -keystore my-release-key.keystore my_application.apk mykey
驗證簽名: $ jarsigner -verify -verbose -certs my_application.apk
在進行Android二次開發時,有時需要把build/target/proct/security下面的公私鑰對轉換為keystore的形式,可以參考這篇文章:把Android源碼中的密碼對轉換為keystore的方法。
⑶ Android V1及V2簽名原理簡析
Android為了保證系統及應用的安全性,在安裝APK的時候需要校驗包的完整性,同時,對於覆蓋安裝的場景還要校驗新舊是否匹配,這兩者都是通過Android簽名機制來進行保證的,本文就簡單看下Android的簽名與校驗原理,分一下幾個部分分析下:
簽名是摘要與非對稱密鑰加密相相結合的產物,摘要就像內容的一個指紋信息,一旦內容被篡改,摘要就會改變,簽名是摘要的加密結果,摘要改變,簽名也會失效。Android APK簽名也是這個道理,如果APK簽名跟內容對應不起來,Android系統就認為APK內容被篡改了,從而拒絕安裝,以保證系統的安全性。目前Android有三種簽名V1、V2(N)、V3(P),本文只看前兩種V1跟V2,對於V3的輪密先不考慮。先看下只有V1簽名後APK的樣式:
再看下只有V2簽名的APK包樣式:
同時具有V1 V2簽名:
可以看到,如果只有V2簽名,那麼APK包內容幾乎是沒有改動的,META_INF中不會有新增文件,按Google官方文檔:在使用v2簽名方案進行簽名時,會在APK文件中插入一個APK簽名分塊,該分塊位於zip中央目錄部分之前並緊鄰該部分。在APK簽名分塊內, 簽名和簽名者身份信息會存儲在APK簽名方案v2分塊中,保證整個APK文件不可修改 ,如下圖:
而V1簽名是通過META-INF中的三個文件保證簽名及信息的完整性:
V1簽名是如何保證信息的完整性呢?V1簽名主要包含三部分內容,如果狹義上說簽名跟公鑰的話,僅僅在.rsa文件中,V1簽名的三個文件其實是一套機制,不能單單拿一個來說事,
如果對APK中的資源文件進行了替換,那麼該資源的摘要必定發生改變,如果沒有修改MANIFEST.MF中的信息,那麼在安裝時候V1校驗就會失敗,無法安裝,不過如果篡改文件的同時,也修改其MANIFEST.MF中的摘要值,那麼MANIFEST.MF校驗就可以繞過。
CERT.SF個人覺得有點像冗餘,更像對文件完整性的二次保證,同繞過MANIFEST.MF一樣,.SF校驗也很容易被繞過。
CERT.RSA與CERT.SF是相互對應的,兩者名字前綴必須一致,不知道算不算一個無聊的標准。看下CERT.RSA文件內容:
CERT.RSA文件裡面存儲了證書公鑰、過期日期、發行人、加密演算法等信息,根據公鑰及加密演算法,Android系統就能計算出CERT.SF的摘要信息,其嚴格的格式如下:
從CERT.RSA中,我們能獲的證書的指紋信息,在微信分享、第三方SDK申請的時候經常用到,其實就是公鑰+開發者信息的一個簽名:
除了CERT.RSA文件,其餘兩個簽名文件其實跟keystore沒什麼關系,主要是文件自身的摘要及二次摘要,用不同的keystore進行簽名,生成的MANIFEST.MF與CERT.SF都是一樣的,不同的只有CERT.RSA簽名文件。也就是說前兩者主要保證各個文件的完整性,CERT.RSA從整體上保證APK的來源及完整性,不過META_INF中的文件不在校驗范圍中,這也是V1的一個缺點。V2簽名又是如何保證信息的完整性呢?
前面說過V1簽名中文件的完整性很容易被繞過,可以理解 單個文件完整性校驗的意義並不是很大 ,安裝的時候反而耗時,不如採用更加簡單的便捷的校驗方式。V2簽名就不針對單個文件校驗了,而是 針對APK進行校驗 ,將APK分成1M的塊,對每個塊計算值摘要,之後針對所有摘要進行摘要,再利用摘要進行簽名。
也就是說,V2摘要簽名分兩級,第一級是對APK文件的1、3 、4 部分進行摘要,第二級是對第一級的摘要集合進行摘要,然後利用秘鑰進行簽名。安裝的時候,塊摘要可以並行處理,這樣可以提高校驗速度。
APK是先摘要,再簽名,先看下摘要的定義:Message Digest:摘要是對消息數據執行一個單向Hash,從而生成一個固定長度的Hash值,這個值就是消息摘要,至於常聽到的MD5、SHA1都是摘要演算法的一種。理論上說,摘要一定會有碰撞,但只要保證有限長度內碰撞率很低就可以,這樣就能利用摘要來保證消息的完整性,只要消息被篡改,摘要一定會發生改變。但是,如果消息跟摘要同時被修改,那就無從得知了。
而數字簽名是什麼呢(公鑰數字簽名),利用非對稱加密技術,通過私鑰對摘要進行加密,產生一個字元串,這個字元串+公鑰證書就可以看做消息的數字簽名,如RSA就是常用的非對稱加密演算法。在沒有私鑰的前提下,非對稱加密演算法能確保別人無法偽造簽名,因此數字簽名也是對發送者信息真實性的一個有效證明。不過由於Android的keystore證書是自簽名的,沒有第三方權威機構認證,用戶可以自行生成keystore,Android簽名方案無法保證APK不被二次簽名。
知道了摘要跟簽名的概念後,再來看看Android的簽名文件怎麼來的?如何影響原來APK包?通過sdk中的apksign來對一個APK進行簽名的命令如下:
其主要實現在 android/platform/tools/apksig 文件夾中,主體是ApkSigner.java的sign函數,函數比較長,分幾步分析
先來看這一步,ApkUtils.findZipSections,這個函數主要是解析APK文件,獲得ZIP格式的一些簡單信息,並返回一個ZipSections,
ZipSections包含了ZIP文件格式的一些信息,比如中央目錄信息、中央目錄結尾信息等,對比到zip文件格式如下:
獲取到 ZipSections之後,就可以進一步解析APK這個ZIP包,繼續走後面的簽名流程,
可以看到先進行了一個V2簽名的檢驗,這里是用來簽名,為什麼先檢驗了一次?第一次簽名的時候會直接走這個異常邏輯分支,重復簽名的時候才能獲到取之前的V2簽名,懷疑這里獲取V2簽名的目的應該是為了排除V2簽名,並獲取V2簽名以外的數據塊,因為簽名本身不能被算入到簽名中,之後會解析中央目錄區,構建一個DefaultApkSignerEngine用於簽名
先解析中央目錄區,獲取AndroidManifest文件,獲取minSdkVersion(影響簽名演算法),並構建DefaultApkSignerEngine,默認情況下V1 V2簽名都是打開的。
第五步與第六步的主要工作是:apk的預處理,包括目錄的一些排序之類的工作,應該是為了更高效處理簽名,預處理結束後,就開始簽名流程,首先做的是V1簽名(默認存在,除非主動關閉):
步驟7、8、9都可以看做是V1簽名的處理邏輯,主要在V1SchemeSigner中處理,其中包括創建META-INFO文件夾下的一些簽名文件,更新中央目錄、更新中央目錄結尾等,流程不復雜,不在贅述,簡單流程就是:
這里特殊提一下重復簽名的問題: 對一個已經V1簽名的APK再次V1簽名不會有任何問題 ,原理就是:再次簽名的時候,會排除之前的簽名文件。
可以看到目錄、META-INF文件夾下的文件、sf、rsa等結尾的文件都不會被V1簽名進行處理,所以這里不用擔心多次簽名的問題。接下來就是處理V2簽名。
V2SchemeSigner處理V2簽名,邏輯比較清晰,直接對V1簽名過的APK進行分塊摘要,再集合簽名,V2簽名不會改變之前V1簽名後的任何信息,簽名後,在中央目錄前添加V2簽名塊,並更新中央目錄結尾信息,因為V2簽名後,中央目錄的偏移會再次改變:
簽名校驗的過程可以看做簽名的逆向,只不過覆蓋安裝可能還要校驗公鑰及證書信息一致,否則覆蓋安裝會失敗。簽名校驗的入口在PackageManagerService的install里,安裝官方文檔,7.0以上的手機優先檢測V2簽名,如果V2簽名不存在,再校驗V1簽名,對於7.0以下的手機,不存在V2簽名校驗機制,只會校驗V1,所以,如果你的App的miniSdkVersion<24(N),那麼你的簽名方式必須內含V1簽名:
校驗流程就是簽名的逆向,了解簽名流程即可,本文不求甚解,有興趣自己去分析,只是額外提下覆蓋安裝,覆蓋安裝除了檢驗APK自己的完整性以外,還要校驗證書是否一致只有證書一致(同一個keystore簽名),才有可能覆蓋升級。覆蓋安裝同全新安裝相比較多了幾個校驗
這里只關心證書部分:
Android V1及V2簽名簽名原理簡析
僅供參考,歡迎指正
⑷ 如何獲取android 簽名信息
android中有時候需要獲取應用的簽名信息,簽名信息一般有:公鑰,演算法名,MD5值,序列號。要想獲取這些信息首先該APK應用要有系統許可權。
1. 在配置文件里添加系統許可權:android:sharedUserId="android.uid.system"
2. 獲取簽名信息的字元信息:
String signature_key = "";
try{
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(pkgName,
PackageManager.GET_SIGNATURES);
Signature[] signatures =
packageInfo.signatures;
SignatureKey.parseSignature(signatures[0].toByteArray());
} catch (NameNotFoundException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
(1).其中packageInfo 得到的是指定包名的簽名信息的字元串。另提及多說一句:在程序中我們一般使用
List list =
mPackageManager.getInstalledPackages(0);來得到所有的安裝包的信息,這其中包括應用名,包名,大小....
(2).
其中[]signatures里是存放的簽名的字元串數組。我們一般取用第一個signatures[0]來做為該應用的簽名信息。這里有一個疑惑就是為什麼得到的是一個字元串的數組列表,而不是一個字元串,也有的開發者採用:
StringBuilder builder = new StringBuilder();
for(Signature sign: signatures
){
builder.append(sign.toCharsString());
builder.append("/n");
}來得到所有的簽名信息。在這里我驗證過,signatures
的長度為1,所以指定apk的簽名信息就為signatures[0]。
至於第二種方法有待發現。
3 .獲取簽名的MD5值
public static final String getMD5String(byte[] paramArrayOfByte)
{
char[] asciiTable = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
97, 98, 99, 100, 101, 102 }; // ascii表對應的數字和字元的編碼
try
{
MessageDigest md5MessageDigest =
MessageDigest.getInstance("MD5");
md5MessageDigest.update(paramArrayOfByte);//
byte[]
tempByte = md5MessageDigest.digest();
int i =
tempByte.length;
char[] tempChar = new char[i *
2];
int j = 0;
int k =
0;
while (true) { //
將二進制數組轉換成字元串
if (j >= i)
{
return new
String(tempChar);
}
int m
= tempByte[j];
int n = k +
1;
tempChar[k] = asciiTable[(0xF & m >>>
4)];
k = n +
1;
tempChar[n] = asciiTable[(m &
0xF)];
j++;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return
null;
}
(1). 其中參數就是上一步得到的簽名的byte數組。
4. 獲取公鑰,簽名演算法,簽名序列號
public static void parseSignature(byte[] signature)
{
try
{
CertificateFactory
certFactory =
CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new
ByteArrayInputStream(signature));
String pubKey =
cert.getPublicKey().toString(); //公鑰
String signNumber =
cert.getSerialNumber().toString();
System.out.println("signName:" +
cert.getSigAlgName());//演算法名
System.out.println("pubKey:" +
pubKey);
System.out.println("signNumber:" +
signNumber);//證書序列編號
System.out.println("subjectDN:"+cert.getSubjectDN().toString());
} catch (CertificateException e)
{
e.printStackTrace();
}
}
5. 電腦查看APK簽名信息
查看三方應用或是系統應用簽名
用winrar打開待查看的apk,將其中META-INF文件夾解壓出來,得到其中的CERT.RSA文件
1.keytool
-printcert -file META-INF/CERT.RSA
命令是:keytool -printcert -file
<簽名文件RSA的路徑>
2.jarsigner -verify -verbose -certs Superuser.apk
此條未驗證成功
⑸ 如何對Android的APP進行簽名
1、在Android Studio中打開工程,點擊「Build」菜單下的「Generate Signed APK」。