Android apk完整性檢測的實現(xiàn)思路和代碼實現(xiàn)
需求和背景
行業(yè)相關(guān),對安全性較高的程序一般都需要添加完整性檢測的功能,以防止程序被篡改,從而導(dǎo)致安全問題的發(fā)生。
相關(guān)的支付應(yīng)用項目今年也做了好幾個,這些程序也都已通過了行業(yè)相關(guān)安全標(biāo)準(zhǔn)的認(rèn)證。
實現(xiàn)
下面來分享Android APP完整性校驗的實現(xiàn)思路和代碼實現(xiàn)。
通過sp判斷當(dāng)前是否是第一次安裝apk,第一次安裝默認(rèn)apk是從市場下載安裝,默認(rèn)認(rèn)為是沒有被篡改過的??梢圆挥脵z查,只計算當(dāng)前的hash值并保存到文件中。
可以在application中執(zhí)行,計算apk的hash值并寫文件的操作是耗時操作,記得開子線程進行。
private boolean integrityCheckResult = false;
private boolean isFirstRun;//可以通過文件保存,例如SP
@Override
public void onCreate() {
super.onCreate();
ThreadPoolManager.getInstance().runInBackground(new Runnable() {
@Override
public void run() {
//檢測apk完整性
if (isFirstRun){//skip and calculate apk's hash
SecurityManager.getInstance().checkIntegrity(true);
integrityCheckResult = true;
}else {
integrityCheckResult = SecurityManager.getInstance().checkIntegrity(false);
}
}
});
public boolean isIntegrityCheckResult() {
return integrityCheckResult;
}在入口activity中判斷是否完整性校驗通過,假如不通過,可以彈窗提示然后鎖定APP,讓用戶重新在安全的平臺重新下載安裝。當(dāng)前APP無法使用,存在安全問題。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (App.getApp().isIntegrityCheckResult()) {
Log.d(TAG, "onCreate: checkIntegrity success");
} else {
Log.d(TAG, "onCreate: checkIntegrity failed");
}
}安全管理類
新建一個安全管理類,用于管理所有和安全相關(guān)的類
public class SecurityManager {
//做一個單例
private static SecurityManager instance = null;
private final Integrity integrity;
private SecurityManager(){
integrity = new Integrity();
}
public static synchronized SecurityManager getInstance() {
if (instance == null)
instance = new SecurityManager();
return instance;
}
public boolean checkIntegrity(boolean isFirstInstall) {
return integrity.checkIntegrity(isFirstInstall);
}
}實現(xiàn)完整性檢測類的接口
public interface IIntegrity {
boolean checkApkIntegrity();
}完整性檢測實現(xiàn)類:
public class Integrity implements IIntegrity {
public boolean checkIntegrity(boolean isFirstInstall) {
if (isFirstInstall) {
calcAndSaveApkSoHash();
return true;
} else {
return compareHashsWithLastTime();
}
}
private void calcAndSaveApkSoHash() {
File apk = new File(BaseApplication.getAppContext().getPackageCodePath());
byte[] apkHash = HashCalculator.calculateHashBytes(apk, HashCalculator.SHA_256);
FileUtils.writeBytesToFile(filePath + APK_HASH_FILE, apkHash);
}
private boolean compareHashsWithLastTime() {
//檢測apk so
return checkApkIntegrity();
}
@Override
public boolean checkApkIntegrity() {
if (BuildConfig.DEBUG) {
Log.w(TAG, "Debug version,skip apk‘s hash verification");
return true;
}
try {
String apkPath = BaseApplication.getAppContext().getPackageCodePath();
byte[] originalApkHash = FileUtils.readFileToBytes(filePath + APK_HASH_FILE);
return calcSrcAndCompareWithLastHash(originalApkHash, new File(apkPath));
} catch (IOException e) {
Log.e(TAG, "checkApkAndLibs: ", e);
}
return false;
}
/**
* 計算明文數(shù)據(jù)并和上一次hash進行比較
*
* @param decHashBytes 明文hash數(shù)據(jù)
* @param decSrc 明文源數(shù)據(jù)
*/
private static boolean calcSrcAndCompareWithLastHash(byte[] decHashBytes, File decSrc) {
String decHash = Utils.bcd2Str(decHashBytes);
//計算解密ksn的hash
String calcHash = HashCalculator.calculateHash(decSrc, HashCalculator.SHA_256);
LogUtils.i(TAG,
"calculate hash = " + Utils.bcd2Str(
HashCalculator.calculateHashBytes(decSrc, HashCalculator.SHA_256)));
return decHash.equalsIgnoreCase(calcHash);
}
}相關(guān)工具類
這個只是工具類,方便獲取Application ,只要獲取context即可,可以隨意發(fā)揮。
public class BaseApplication extends Application {
private static BaseApplication mBaseApplication ;
mBaseApplication = this;
}
public static BaseApplication getAppContext(){
return mBaseApplication;
}編碼轉(zhuǎn)換工具:
@NonNull
public static String bcd2Str(@Nullable byte[] b, int length) {
if (b == null) {
return "";
}
StringBuilder sb = new StringBuilder(length * 2);
for (int i = 0; i < length; ++i) {
sb.append(ARRAY_OF_CHAR[((b[i] & 0xF0) >>> 4)]);
sb.append(ARRAY_OF_CHAR[(b[i] & 0xF)]);
}
return sb.toString();
}hash計算器
@NonNull
public static String bcd2Str(@Nullable byte[] b, int length) {
if (b == null) {
return "";
}
StringBuilder sb = new StringBuilder(length * 2);
for (int i = 0; i < length; ++i) {
sb.append(ARRAY_OF_CHAR[((b[i] & 0xF0) >>> 4)]);
sb.append(ARRAY_OF_CHAR[(b[i] & 0xF)]);
}
return sb.toString();
}文件工具類
/**
* 文件鎖定(File Locking)
* 強制刷新緩沖(Force Flushing Buffer):
*/
public static boolean writeBytesToFile(String filePath, byte[] bytes) {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(bytes);
// 獲取文件鎖定
FileChannel fileChannel = fos.getChannel();
try (FileLock fileLock = fileChannel.lock()) {
// 強制刷新緩沖
fileChannel.force(true);
}
return true;
} catch (IOException e) {
LogUtils.e(e);
return false;
}
}到此這篇關(guān)于Android apk完整性檢測的實現(xiàn)思路和實現(xiàn)過程全記錄的文章就介紹到這了,更多相關(guān)Android apk完整性檢測內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實現(xiàn)簡易計步器功能隔天步數(shù)清零查看歷史運動紀(jì)錄
這篇文章主要介紹了Android實現(xiàn)簡易計步器功能隔天步數(shù)清零查看歷史運動紀(jì)錄,需要的朋友可以參考下2017-06-06
Android開發(fā)之APP安裝后在桌面上不顯示應(yīng)用圖標(biāo)的解決方法
這篇文章主要介紹了Android開發(fā)之APP安裝后在桌面上不顯示應(yīng)用圖標(biāo)的解決方法,涉及Android activity相關(guān)屬性設(shè)置技巧,需要的朋友可以參考下2017-07-07
Android 新聞界面模擬ListView和ViewPager的應(yīng)用
本文主要介紹 Android ListView和ViewPager的應(yīng)用,這里模擬了新聞界面及實現(xiàn)示例代碼,有需要的小伙伴可以參考下2016-09-09
Android中使用include標(biāo)簽和merge標(biāo)簽重復(fù)使用布局
這篇文章主要介紹了Android中使用include標(biāo)簽和merge標(biāo)簽重復(fù)使用布局,文中講解了創(chuàng)建可復(fù)用布局的例子以及include標(biāo)簽和merge標(biāo)簽使用例子,需要的朋友可以參考下2014-06-06
Android 實現(xiàn)密碼輸入框動態(tài)明文/密文切換顯示效果
在項目中遇到需要提供給用戶一個密碼輸入框明文/密文切換顯示的需求,今天小編借腳本之家平臺給大家分享下Android 實現(xiàn)密碼輸入框動態(tài)明文/密文切換顯示效果,需要的朋友參考下2017-01-01

