安卓數字簽名指的是對apk包做文件摘要並加密,在安裝apk包時做解密和驗證以保證包體不被篡改。這里先普及下簽名和驗證流程。簽名文件保存在apk包里META-INF目錄下,包含3個文件:
1、后綴為MF的是摘要文件。首先遍歷apk包,將除META-INF目錄外其他所有文件用SHA1生成摘要信息並用base64編碼。如果你手動改變了apk包中的文件,那么在apk包安裝驗證時,改后的文件摘要信息與原MF文件中的不一致,會導致安裝失敗。
2、后綴為SF還是摘要文件。對上面生成的MF文件做兩步處理,首先讀取全量摘要文件使用SHA1生成摘要並用base64編碼,然后再次讀取各文件子項再次做一樣的摘要操作。
3、后綴為RSA的是簽名文件。數字證書一般存放在鑰匙庫中,從數字證書中取出私鑰,對SF文件使用SHA1-RSA算法進行非對稱加密得到RSA文件。在安裝時從鑰匙庫取出數字證書的公鑰並解密,再與未加密前的摘要信息進行對比,相符則說明apk包未被篡改。
問題場景是原來簽名正常的apk包突然出現閃退現象。經定位發現該簽名包體原來已經簽過名,重復簽名導致問題發生。排查代碼發現,在生成MF文件時調用addDigestsToMainfest方法時沒有去掉以上三個文件,導致重復簽名時重復計算摘要,安裝驗證時拿計算后的摘要比較計算前的,必然會出現驗證失敗。
舉例,首次簽名時包里是不存在這三個文件的,所以首次簽名會正常生成wlf. MF、wlf.SF和wlf.RSA;第二次簽名會把這三個文件也計算進wlf.MF里去,接着按上面邏輯生成另外兩個文件,再替換掉原來三個同名文件。而且這時wlf.MF已經改變了,同樣其他兩個文件也跟着變。那么在驗證時拿新的摘要去比對包體里老的摘要,發現這三個文件被替換了,驗證也就失敗了。第三次重復簽名的話這三個文件還會被替換成新的,因為計算后文件又被替換了,以此類推。舉個例子,好比你拿着剛辦好的二代身份證給人家看,但人家系統里只有你的一代身份證,雖然名字一樣,樣貌衣着卻變了,人家一核對就懷疑你了。而篡改的人就是你自己,這就叫監守自盜了。所以做摘要必須要去掉那三個文件。