Android apk完整性检测的实现思路和代码实现
需求和背景
行业相关,对安全性较高的程序一般都需要添加完整性检测的功能,以防止程序被篡改,从而导致安全问题的发生。
相关的支付应用项目今年也做了好几个,这些程序也都已通过了行业相关安全标准的认证。
实现
下面来分享Android APP完整性校验的实现思路和代码实现。
通过sp判断当前是否是第一次安装apk,第一次安装默认apk是从市场下载安装,默认认为是没有被篡改过的。可以不用检查,只计算当前的hash值并保存到文件中。
可以在application中执行,计算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,让用户重新在安全的平台重新下载安装。当前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"); } }
安全管理类
新建一个安全管理类,用于管理所有和安全相关的类
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); } }
实现完整性检测类的接口
public interface IIntegrity { boolean checkApkIntegrity(); }
完整性检测实现类:
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; } /** * 计算明文数据并和上一次hash进行比较 * * @param decHashBytes 明文hash数据 * @param decSrc 明文源数据 */ 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); } }
相关工具类
这个只是工具类,方便获取Application ,只要获取context即可,可以随意发挥。
public class BaseApplication extends Application { private static BaseApplication mBaseApplication ; mBaseApplication = this; } public static BaseApplication getAppContext(){ return mBaseApplication; }
编码转换工具:
@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; } }
到此这篇关于Android apk完整性检测的实现思路和实现过程全记录的文章就介绍到这了,更多相关Android apk完整性检测内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
浅谈Android为RecyclerView增加监听以及数据混乱的小坑
下面小编就为大家带来一篇浅谈Android为RecyclerView增加监听以及数据混乱的小坑。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2017-04-04Anroid ListView分组和悬浮Header实现方法
这篇文章主要介绍了Anroid ListView分组和悬浮Header实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。2016-11-11
最新评论