概述
根据前面的文章来看,JNI其实只实现了关键代码加密,如果别人拿到了你的Java Native方法定义和对应的so,即可完成对你so里方法的调。因为native 方法和类都是不能混淆的,混淆了方法的函数名就变了,调用的时候就找不到方法了,因此如果反编译APK可以非常容易拿到相关文件和代码。 显然我们需要一些手段来在JNI的验证请求接口的是不是我们的程序。
签名验证的原理
可以用如下图来表明加了验证之后调用JNI的逻辑,用一个isValid
来表明请求的应用是不是我们自己的应用。isValid
通过init
去初始化。
如何判别调用者的有效性
直接有效的方案就是使用签名进行判定,如果你的keystore没有泄漏,第三方破解概率几乎为零。当然这都是相对的,任何防护都会有破绽。大多数第三方的Android SDK也都通过签名来判断申请的key是否用在了你申请的应用上。因此在大多数SDK申请key的时候会让你填写SHA1,因为在程序运行的时候SDK会获取你签名的SHA1去向服务器验证,你申请的appkey和SHA1是否正确。
签名验证的实现
在代码中获取签名的SHA1
先尝试在Java代码中获取签名的SHA1
private String getCertSHA1(Context context)
{
try
{
//获取包管理器
PackageManager packageManager=context.getPackageManager();
//获取包名
String packageName=context.getPackageName();
//获得包信息
PackageInfo pis = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
//获得签名
Signature[] signs = pis.signatures; //签名
//获得签名数组的第一位
Signature sign=signs[0];
//获得X.509证书工厂
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
byte[] signBytes=sign.toByteArray();
ByteArrayInputStream byteIn=new ByteArrayInputStream(signBytes);
//获取X509证书
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(byteIn);
//获取证书发行者SHA1
MessageDigest sha1=MessageDigest.getInstance("SHA1");
byte[] certByte=cert.getEncoded();
byte[] bs=sha1.digest (certByte);
return toHex(bs);
}
catch (CertificateException e)
{
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
//将Byte转换成HexString 辅助函数
char[] cs=new char[16];
{
for (int i=0;i < 10;i++)
{
cs[i] = (char) ('0' + i);
}
for (int i=10;i < 16;i++)
{
cs[i] = (char) ('A' + i - 10);
}
}
String toHex(byte[] bs)
{
char[] cs=new char[bs.length * 2];
int x;
for (int i=0;i < bs.length;i++)
{
x = bs[i] & 0xff;
cs[2 * i] = this<