總結:
1、在傳輸過程中對JS檔案進行了RSA簽名加密;
2、下載完指令碼儲存到本地時,應進行簡單的對稱式加密,每次讀取時解密;
3、建議js指令碼的增、刪、改、查的內容在同一個js檔案處理,只下載一個檔案就OK。
繼上一篇瞭解工作原理後,這篇談談一下自行搭建管理後台實現補丁更新的基本思路。
先假設遇到以下問題:App發行了兩個版本,V1.0和V2.0,上線投產後發現V1.0存在bug1,V2.0存在bug2;
前提準備:
a、本地產生RSA金鑰組,公開金鑰可以公開,打包作為配置常量放進ipa檔案,私密金鑰自己保管好,存放在如PSAM卡、帶加密功能的磁碟載體等。
b、製作好的指令檔(即補丁)。
基本原理:安裝本地所有補丁 –> 連網更新補丁資訊,並安裝有更新或新增加的補丁。
具體流程:
1、在管理後台建立需要更新補丁的版本號碼,然後上傳bug檔案(*.js)。上傳指令碼時,選擇本地的 rsa_private_key.pem (RSA私密金鑰)檔案,與指令碼一同上傳,管理平台會使用這個上傳的 Private Key 對指令碼 MD5 值進行加密,得到加密結果並與該指令檔一一對應儲存起來。
2、用戶端判斷本地是否有指令碼,有則在程式開始的時候載入執行(若檔案對稱式加密了需先解密),無則跳過;
3、用戶端連網請求補丁更新,上傳時參數帶版本資訊(如版本號碼),查詢目前的版本是否有bug。
4、管理平台會只針對這個版本號碼下發對應的 JS 指令碼(實現對V1.0和V2.0分別處理),若版本號碼對應不上或無bug,用戶端也就請求不到相應的 JS 指令碼;若有bug,由伺服器返回step1的指令碼和加密MD5值。
5、用戶端計算指令碼的MD5值(MD5_1),通過內建的RSA公開金鑰解密 step4 的MD5得到值MD5_2,比較MD5_1和MD5_2是否相同,能夠解密得出來並結果相同,說明加密者一定是使用了step1中RSA私密金鑰,伺服器返回資料可信,先儲存到App本機快取(儲存到本地時可進行簡單的對稱式加密,每次讀取時解密)。
6、以後App每次啟動,都需要載入並執行本地已儲存好的這個指令碼(即上面的step2),這樣就可以實現補丁更新了。
注意:這裡上傳的 rsa_private_key.pem 只是一次性使用,不會儲存在服務端,所以只有通過使用者自己儲存的 rsa_private_key.pem 檔案才可以針對 APP 下髮腳本,即使管理平台被黑,駭客也無法對你的 APP 下發惡意指令碼(可以下發,但驗證不過,不會執行),保證安全性。rsa_private_key.pem 請妥善保管,避免泄露。
待討論:上面步驟中,補丁檔案沒有強制更新,它只會載入上一次已下載好的指令碼。假設在AppDelegate中載入腳步,且用戶端一直休眠在系統後台沒有重啟,那麼這種方案無法對每一個使用者實現即時修複。有人說,那好辦,我在程式入口處設定必須拉取伺服器指令碼成功後才能進入程式介面,這樣可以強制使用者實現即時修複,我覺得也是有問題的:如果網路不穩定,提取要求失敗,用戶端應該如何呈現。是退出(使用者體驗不好)還是跳轉到程式介面(這樣就無法在本地執行指令碼)呢。
App需要具備 “增、刪、改、查” 功能
其他問題:如果V1.0新發現了bug3;或者發現上一次腳步內容有紕漏需要修改;或者取消這個版本的補丁,用戶端和伺服器應該如何應對。這就要求app需要具備 “增、刪、改、查” 功能。
增:伺服器返回的補丁,本地不存在時,會預設下載儲存,並執行.
刪: 伺服器返回的補丁集中,不包含本地的某個補丁,則此補丁下次不會再被執行.
改: 伺服器返回的補丁,當地套件含,但md5值變化,此時會重新下載此補丁.
查: 會預設在應用啟動時,執行所有存在,且md5值匹配的補丁.補丁集的資訊,會在每次連網更新時更新.此處使用的是一個緩衝庫
補丁狀態的管理詳見點擊開啟連結
最佳化:鑒於上述步驟可能會存在操作複雜性,採用一次性下載所有可能會更好:把增、刪、改、查的內容全部寫到一個js檔案,
1、用戶端每次請求時先搜尋本地是否有js檔案並計算MD5值,若無值則不傳,若有則新增傳MD5值;
2、伺服器判斷MD5 不為空白,進行兩個MD5值比對,不一致下髮腳本;
3、用戶端緩衝指令碼(首次下載)或替換指令碼(本地已緩衝)
這裡要注意刪除指令碼問題,如果後台把所有指令碼刪除了,那麼用戶端也要根據返回的空白內容刪除本機快取。
Mac電腦擷取某個檔案的md5值,直接在終端輸入命令:
md5 檔案完整路徑
Objective-C擷取某個檔案的md5值:
#include <CommonCrypto/CommonDigest.h>:/** * 擷取檔案的md5值. * @param path 檔案路徑. * @return 檔案的md5值. */-(NSString *)mcMd5HashOfPath:(NSString *)path{ NSFileManager *fileManager = [NSFileManager defaultManager]; // 確保檔案存在. if( [fileManager fileExistsAtPath:path isDirectory:nil] ) { NSData *data = [NSData dataWithContentsOfFile:path]; unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5( data.bytes, (CC_LONG)data.length, digest ); NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ ) { [output appendFormat:@"%02x", digest[i]]; } return output; } else { return @""; }}
在工程中放一個demo.js供Debug模式下調試
/** * 測試模式下,會執行此方法,以驗證某個JS檔案的作用.預設使用本地demo.js. */- (void)mcDebug{#ifdef DEBUG NSString * path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"]; [self mcEvaluateScriptFile: path];#endif}
jspatch解決AppStore審查機制解決方案是:點擊開啟連結
參考:點擊開啟連結