SHA1-Collision & Android Sign
參看SHA1-collision我們可以知道茄靠,SHA-1簽名已經(jīng)不安全了窘哈,簽名算法可以考慮升級(jí)到SHA-2或者其他算法匠襟。
0x01 SHA1-Collision
1. SHA-1是什么庇勃?
SHA-1(英語:Secure Hash Algorithm 1陷舅,中文名:安全散列算法1)是一種密碼散列函數(shù)望薄,美國(guó)國(guó)家安全局設(shè)計(jì)疟游,并由美國(guó)國(guó)家標(biāo)準(zhǔn)技術(shù)研究所(NIST)發(fā)布為聯(lián)邦數(shù)據(jù)處理標(biāo)準(zhǔn)(FIPS)[2]。SHA-1可以生成一個(gè)被稱為消息摘要的160位(20字節(jié))散列值痕支,散列值通常的呈現(xiàn)形式為40個(gè)十六進(jìn)制數(shù)颁虐,參考:SHA-1
2. SHA1-Collision是什么?
兩個(gè)內(nèi)容不同的數(shù)據(jù)卧须,SHA-1算法會(huì)生成相同的摘要信息另绩,參考:SHA1 collision Two PDF儒陨。
3. SHA1-Collision對(duì)Android的影響
Android SDK默認(rèn)對(duì)apk使用SHA-1簽名,在最壞的情況下笋籽,攻擊者可以偽造SHA-1值相同的文件替換已簽名apk中的文件來達(dá)到攻擊的目的蹦漠。
0x02 Android的證書驗(yàn)證機(jī)制
我們知道Android的Apk文件是一個(gè)壓縮文件,文件結(jié)構(gòu)大致如下:
.
├── AndroidManifest.xml
├── META-INF
│ ├── CERT.RSA
│ ├── CERT.SF
│ └── MANIFEST.MF
├── assets
├── classes.dex
├── classes2.dex
├── classes3.dex
├── lib
├── res
└── resources.arsc
apk相關(guān)的簽名相關(guān)的文件在META-INF
目錄中车海,其中:
MANIFEST.MF
遍歷APK包中除了META-INF\
文件夾以外的所有文件笛园,利用SHA-1算法生成這些文件的消息摘要,然后轉(zhuǎn)化為對(duì)應(yīng)的base64編碼侍芝。MANIFEST.MF
存儲(chǔ)的是文件的摘要值研铆,保證完整性,防止文件被篡改竭贩。.SF
xx.SF文件(xx為使用者證書的自定義別名蚜印,默認(rèn)為CERT,即CERT.SF)留量,保存的是MANIFEST.MF的摘要值窄赋, 以及MANIFEST.MF中每一個(gè)摘要項(xiàng)的摘要值,然后轉(zhuǎn)化成對(duì)應(yīng)的base64編碼楼熄。雖然該文件的后綴名.sf(SignatureFile)看起來是簽名文件忆绰,但是并沒有私鑰參與運(yùn)算,也不保存任何簽名內(nèi)容可岂。.RSA/.DSA
.RSA/.DSA文件(后綴不同采用的簽名算法不同错敢,.RSA使用的是RSA算法,.DSA使用的是數(shù)字簽名算法DSA缕粹,目前APK主要使用的是這兩種算法)稚茅,保存的是第二項(xiàng).SF文件的數(shù)字簽名,同時(shí)還會(huì)包括簽名采用的數(shù)字證書(公鑰)平斩。特別說明亚享,當(dāng)使用多重證書簽名時(shí),每一個(gè).sf文件必須有一個(gè).RSA/.DSA文件與之對(duì)應(yīng)绘面,也就是說使用證書CERT1簽名時(shí)有CERT1.SF和CERT1.RSA欺税,同時(shí)采用證書CERT2簽名時(shí)又會(huì)生成CERT2.SF和CERT2.RSA。
Android 系統(tǒng)不允許安裝沒有任何數(shù)字簽名的應(yīng)用APK程序揭璃,所有應(yīng)用程序必須使用某個(gè)證書進(jìn)行簽名(一般為應(yīng)用開發(fā)者自簽名證書)晚凿,
APK源文件,首先由應(yīng)用開發(fā)者使用自己的私鑰瘦馍,對(duì)整個(gè)文件進(jìn)行簽名歼秽,生成上述的三個(gè)文件,然后打包成簽名后的APK文件情组;然后發(fā)布到市場(chǎng)哲银。
用戶從市場(chǎng)下載APK安裝文件扛吞,在真正安裝APK前,會(huì)首先驗(yàn)證數(shù)字簽名荆责。具體步驟:
- 首先計(jì)算除META-INF\ 文件夾以外所有文件的SHA1摘要值,同MANIFEST.MF文件中的摘要值做比對(duì)亚脆。如果不同做院,則證明源文件被篡改,驗(yàn)證不通過濒持,拒絕安裝键耕。
- 計(jì)算MANIFEST.MF的摘要值, 以及MANIFEST.MF中每一個(gè)摘要項(xiàng)的摘要值柑营,同.SF文件中的摘要值做比對(duì)屈雄。如果不同,則證明.SF被篡改官套,驗(yàn)證不通過酒奶,拒絕安裝。
- 從.RSA 文件中取出開發(fā)者證書奶赔,然后從證書中提取開發(fā)者公鑰惋嚎,用該公鑰對(duì).SF文件做數(shù)字簽名,并將結(jié)果同.RSA文件中的.SF簽名進(jìn)行比對(duì)站刑。如果不同另伍,則驗(yàn)證不通過,拒絕安裝绞旅。
0x03 Android支持的簽名算法
android 4.3之前不支持SHA1之外的其他簽名算法摆尝,在4.3之后支持了SHA2等算法,詳見:
There is security vs compatibility trade off a few might be interested in. Pre-4.3, Android did not support any signature algorithms except SHA1. With Android >= 4.3, SHA256 support was fixed, and SHA384, SHA512, and ECDSA were added (source). There are still android 2.3.3 (android-10) devices being sold, so anyone interested in backwards compatibility will have to heed this.
測(cè)試?yán)釉斠姡?a target="_blank" rel="nofollow">how-to-migrate-your-android-apps-signing-key
下面是提交給google的bug鏈接:APKs signed using SHA256withRSA or with individual files hashed using SHA-256 fail to install
在android 4.3版本之前的手機(jī)上面安裝使用sha-256簽名的app時(shí)因悲,錯(cuò)誤日志信息大致如下:
adb install -r Downloads/notepad-sha256withrsa-sha256.apk
~/Downloads/notepad-sha256withrsa-sha256.apk: 1 file pushed. 4.3 MB/s (62395 bytes in 0.014s)
pkg: /data/local/tmp/notepad-sha256withrsa-sha256.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]
0x04 簽名生成與查看
- 生成keystore
keytool -genkey -v -keystore test.keystore -alias testkey -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -dname "cn=Test,ou=Test,c=CA" -validity 10000
- 查看APK的簽名算法
keytool -printcert -jarfile notepad-sha1withrsa-sha1.apk
- 查看keystore
keytool -list -v -keystore test.keystore
- jarsigner簽名
jarsigner -keystore mykeystore -storepass password -sigalg SHA256withRSA -digestalg SHA256 my.apk test
0x05 jarsigner與apksigner的區(qū)別
jarsigner是jdk自帶的工具堕汞,apksigner是android sdk自帶的工具(build-tools 24.0.3+版本才擁有)。在android build-tools 24.0.3以前默認(rèn)使用jarsigner對(duì)app進(jìn)行簽名囤捻,在24.0.3版本以及之后使用apksigner進(jìn)行簽名臼朗,其中apksigner簽名算法根據(jù)android的最低版本的不同而不同,jarsigner則可以直接指定簽名算法(見: 上面的jarsigner簽名)蝎土。
tool | minSdkVersion < 18 | minSdkVersion >= 18 |
---|---|---|
apksigner | SHA1withRSA | SHA256withRSA |
apksigner | SHA1withDSA | SHA256withDSA |
apksigner | SHA256withEC |
代碼詳見com.android.apksig.internal.apk.v1.V1SchemeSigner
:
public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm(PublicKey signingKey, int minSdkVersion)
throws InvalidKeyException
{
String keyAlgorithm = signingKey.getAlgorithm();
if ("RSA".equalsIgnoreCase(keyAlgorithm))
{
if (minSdkVersion < 18) {
return DigestAlgorithm.SHA1;
}
return DigestAlgorithm.SHA256;
}
if ("DSA".equalsIgnoreCase(keyAlgorithm))
{
if (minSdkVersion < 21) {
return DigestAlgorithm.SHA1;
}
return DigestAlgorithm.SHA256;
}
if ("EC".equalsIgnoreCase(keyAlgorithm))
{
if (minSdkVersion < 18) {
throw new InvalidKeyException("ECDSA signatures only supported for minSdkVersion 18 and higher");
}
return DigestAlgorithm.SHA256;
}
throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
}
關(guān)于ApkSigner更多信息视哑,請(qǐng)戳~
0x06 升級(jí)簽名算法為SHA-2
綜上所示,我們可以知道App使用簽名算法的地方有兩處誊涯,分別是:
- 使用keytools生成keystore時(shí)指定的算法挡毅。
- 使用jarsigner/apksigner和keystore對(duì)app進(jìn)行簽名時(shí)指定的算法。
這里我們不修改簽名文件keystore的簽名算法暴构,我們只修改簽名App時(shí)使用的簽名算法為SHA-2跪呈,鑒于上面的原因我們需要升級(jí)android app的minSdkVersion >= 18
段磨,下面介紹兩種升級(jí)SHA-2的方法:
- 升級(jí)buildToolsVersion的版本大于等于24.0.3,gradle打包時(shí)會(huì)自動(dòng)調(diào)用apksigner使用SHA256withRSA對(duì)app進(jìn)行簽名耗绿。
- 使用jarsigner對(duì)app進(jìn)行簽名苹支,然后在命令參數(shù)中直接指定簽名算法即可。
0x07 遺留問題
由于keystore未發(fā)生變化误阻,所以使用不同簽名算法的app是可以互相覆蓋的债蜜,故而攻擊者也可以使用舊版本的apk(使用SHA-1)覆蓋新版本apk(使用SHA-2)繼續(xù)進(jìn)行攻擊,所以為了避免被攻擊者進(jìn)行攻擊的最好更換keystore究反,但是這樣就沒法覆蓋安裝了寻定,詳細(xì)請(qǐng)參考things-that-cannot-change。