提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
为了防止apk被恶意反编译、篡改、二次开发,我们在安装apk程序的时候,系统需要对apk做完整性校验,目前主流的完整性校验包括客户端打包校验、crc32文件校验和apk自身完整性校验。
提示:以下是本篇文章正文内容
一、客户端打包校验
这种校验方式,只能校验打包的key值一致性,如果反编译篡改用相同密钥进行打包签名则无法校验,所以存在局限性.
步骤一、
事先拿到apk签名key值的SHA1(证书指纹),通过输入命令:keytool -v -list -keystore
文件路径.jks
,这里的文件路径就是指向你的key的一个文件路径.
步骤二、
在代码里获取apk签名key值的SHA1(证书指纹)
public static String sHA1(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_SIGNATURES);
byte[] cert = info.signatures[0].toByteArray();
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(cert);
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < publicKey.length; i++) {
String appendString = Integer.toHexString(0xFF & publicKey[i])
.toUpperCase(Locale.US);
if (appendString.length() == 1)
hexString.append("0");
hexString.append(appendString);
hexString.append(":");
}
String result = hexString.toString();
return result.substring(0, result.length()-1);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
步骤三、
校验两个key之的SHA1(签名指纹)
/**
* 和key的签名文件对比
* @param s
*/
private void compare(String s){
if (!s.equals("你获取的SHA1")){
killAppProcess();
}
}
/**
* 杀死进程
*/
public void killAppProcess()
{
//注意:不能先杀掉主进程,否则逻辑代码无法继续执行,需先杀掉相关进程最后杀掉主进程
ActivityManager mActivityManager = (ActivityManager)LoadingActivity.this.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> mList = mActivityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : mList)
{
if (runningAppProcessInfo.pid != android.os.Process.myPid())
{
android.os.Process.killProcess(runningAppProcessInfo.pid);
}
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
二、crc32文件校验
apk主要的逻辑功能都是通过classes.dex文件实现的,所以对classes.dex文件进行完整性校验,可以防止apk被逻辑篡改.
步骤一、运行以下程序先打印出完整的.dex文件
的crc32值.
步骤二、将正确的crc32值储存在资源文件字符串里classesdex_crc,或者服务器存储crc32值.
步骤三、再次运行以下程序,判断apk的crc32值是否正确.正确则打印log:Dex hasn't beenmodified!
,否则log:Dex has beenmodified!
public class MainActivity extendsActivity {
@Override
protected void onCreate(BundlesavedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String apkPath = getPackageCodePath();
Long dexCrc = Long.parseLong(getString(R.string.classesdex_crc));
try
{
ZipFile zipfile = new ZipFile(apkPath);
ZipEntry dexentry = zipfile.getEntry( "classes.dex" );
Log.i( "verification" , "classes.dexcrc=" +dexentry.getCrc());
if (dexentry.getCrc() != dexCrc){
Log.i( "verification" , "Dexhas been modified!" );
} else {
Log.i( "verification" , "Dex hasn't been modified!" );
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
三、apk自身完整性校验
对整个apk进行完整性校验,所以对apk生成的哈希值SHA1不能储存在资源文件中,需要储存在服务器或其它地方.
步骤一、先在linux
下计算出正确的apk_hash
值,用sha1 sum
命令,sha1sum verification.apk
步骤二、将步骤一生成的apk_hash值储存在服务器上,之后每次运行程序都用该apk_hash值和服务器取出的正确的hash值比较.
程序生成和比较hash值代码:
public class MainActivity extendsActivity {
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String apkPath = getPackageCodePath();
MessageDigest msgDigest = null ;
try {
msgDigest = MessageDigest.getInstance( "SHA-1" );
byte [] bytes = new byte [ 1024 ];
int byteCount;
FileInputStream fis = new FileInputStream( new File(apkPath));
while ((byteCount = fis.read(bytes)) > 0 )
{
msgDigest.update(bytes, 0 , byteCount);
}
BigInteger bi = new BigInteger( 1 , msgDigest.digest());
String sha = bi.toString( 16 );
fis.close();
//这里添加从服务器中获取哈希值而后进行对比校验
} catch (Exception e) {
e.printStackTrace();
}
}
}